mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 16:23:17 +02:00
249 lines
7.5 KiB
C++
249 lines
7.5 KiB
C++
//
|
|
// UrlReader.h
|
|
// hifi
|
|
//
|
|
// Created by Tobias Schwinger on 3/21/13.
|
|
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
|
//
|
|
|
|
#ifndef __hifi__UrlReader__
|
|
#define __hifi__UrlReader__
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
/**
|
|
* UrlReader class that encapsulates a context for sequential data retrieval
|
|
* via URLs. Use one per thread.
|
|
*/
|
|
class UrlReader {
|
|
|
|
void* _ptrImpl;
|
|
char* _arrXtra;
|
|
char const* _strError;
|
|
void* _ptrStream;
|
|
size_t _valXtraSize;
|
|
|
|
public:
|
|
|
|
/**
|
|
* Constructor - performs initialization, never throws.
|
|
*/
|
|
UrlReader();
|
|
|
|
/**
|
|
* Destructor - frees resources, never throws.
|
|
*/
|
|
~UrlReader();
|
|
|
|
/**
|
|
* Reads data from an URL and forwards it to the instance of a class
|
|
* fulfilling the ContentStream concept.
|
|
*
|
|
* The call protocol on the ContentStream is detailed as follows:
|
|
*
|
|
* 1. begin(char const* url,
|
|
* char const* content_type, uint64_t bytes, uint64_t stardate)
|
|
*
|
|
* All information except 'url' is optional; 'content_type' can
|
|
* be a null pointer - 'bytes' and 'stardate' can be equal to
|
|
* to 'unavailable'.
|
|
*
|
|
* 2. transfer(char* buffer, size_t bytes)
|
|
*
|
|
* Called until all data has been received. The number of bytes
|
|
* actually processed should be returned.
|
|
* Unprocessed data is stored in an extra buffer whose size is
|
|
* given by the constant UrlReader::max_read_ahead - it can be
|
|
* assumed to be reasonably large for on-the-fly parsing.
|
|
*
|
|
* 3. end(bool ok)
|
|
*
|
|
* Called at the end of the transfer.
|
|
*
|
|
* Returns the same success code
|
|
*/
|
|
template< class ContentStream >
|
|
bool readUrl(char const* url, ContentStream& s);
|
|
|
|
/**
|
|
* Returns a pointer to a static C-string that describes the error
|
|
* condition.
|
|
*/
|
|
inline char const* getError() const;
|
|
|
|
/**
|
|
* Can be called by the stream to set a user-defined error string.
|
|
*/
|
|
inline void setError(char const* static_c_string);
|
|
|
|
/**
|
|
* Pointer to the C-string returned by a call to 'readUrl' when no
|
|
* error occurred.
|
|
*/
|
|
static char const* const success;
|
|
|
|
/**
|
|
* Pointer to the C-string returned by a call to 'readUrl' when the
|
|
* initialization has failed.
|
|
*/
|
|
static char const* const error_init_failed;
|
|
|
|
/**
|
|
* Pointer to the C-string returned by a call to 'readUrl' when the
|
|
* transfer has been aborted by the client.
|
|
*/
|
|
static char const* const error_aborted;
|
|
|
|
/**
|
|
* Pointer to the C-string returned by a call to 'readUrl' when
|
|
* leftover input from incomplete processing caused a buffer
|
|
* overflow.
|
|
*/
|
|
static char const* const error_buffer_overflow;
|
|
|
|
/**
|
|
* Pointer to the C-string return by a call to 'readUrl' when the
|
|
* input provided was not completely consumed.
|
|
*/
|
|
static char const* const error_leftover_input;
|
|
|
|
/**
|
|
* Constant of the maximum number of bytes that are buffered
|
|
* between invocations of 'transfer'.
|
|
*/
|
|
static size_t const max_read_ahead;
|
|
|
|
/**
|
|
* Constant representing absent information in the call to the
|
|
* 'begin' member function of the target stream.
|
|
*/
|
|
static int const unavailable = -1;
|
|
|
|
/**
|
|
* Constant for requesting to abort the current transfer when
|
|
* returned by the 'transfer' member function of the target stream.
|
|
*/
|
|
static size_t const abort = ~0u;
|
|
|
|
private:
|
|
// instances of this class shall not be copied
|
|
UrlReader(UrlReader const&); // = delete;
|
|
UrlReader& operator=(UrlReader const&); // = delete;
|
|
|
|
// entrypoints to compiled code
|
|
|
|
typedef size_t transfer_callback(char*, size_t, size_t, void*);
|
|
|
|
bool perform(char const* url, transfer_callback* transfer);
|
|
|
|
void getinfo(char const*& url,
|
|
char const*& type, int64_t& length, int64_t& stardate);
|
|
|
|
// synthesized callback
|
|
|
|
template< class Stream >
|
|
static size_t callback_template(
|
|
char *input, size_t size, size_t nmemb, void* thiz);
|
|
};
|
|
|
|
template< class ContentStream >
|
|
bool UrlReader::readUrl(char const* url, ContentStream& s) {
|
|
if (! _ptrImpl) return false;
|
|
_strError = success;
|
|
_ptrStream = & s;
|
|
_valXtraSize = ~size_t(0);
|
|
this->perform(url, & callback_template<ContentStream>);
|
|
s.end(_strError == success);
|
|
return _strError == success;
|
|
}
|
|
|
|
inline char const* UrlReader::getError() const { return this->_strError; }
|
|
|
|
inline void UrlReader::setError(char const* static_c_string) {
|
|
|
|
if (this->_strError == success)
|
|
this->_strError = static_c_string;
|
|
}
|
|
|
|
template< class Stream >
|
|
size_t UrlReader::callback_template(
|
|
char *input, size_t size, size_t nmemb, void* thiz) {
|
|
|
|
size *= nmemb;
|
|
|
|
UrlReader* me = static_cast<UrlReader*>(thiz);
|
|
Stream* stream = static_cast<Stream*>(me->_ptrStream);
|
|
|
|
// first call?
|
|
if (me->_valXtraSize == ~size_t(0)) {
|
|
|
|
me->_valXtraSize = 0u;
|
|
// extract meta information and call 'begin'
|
|
char const* url, * type;
|
|
int64_t length, stardate;
|
|
me->getinfo(url, type, length, stardate);
|
|
stream->begin(url, type, length, stardate);
|
|
}
|
|
|
|
size_t input_offset = 0u;
|
|
|
|
while (true) {
|
|
|
|
char* buffer = input + input_offset;
|
|
size_t bytes = size - input_offset;
|
|
|
|
// data in extra buffer?
|
|
if (me->_valXtraSize > 0) {
|
|
|
|
// fill extra buffer with beginning of input
|
|
size_t fill = max_read_ahead - me->_valXtraSize;
|
|
if (bytes < fill) fill = bytes;
|
|
memcpy(me->_arrXtra + me->_valXtraSize, buffer, fill);
|
|
// use extra buffer for next transfer
|
|
buffer = me->_arrXtra;
|
|
bytes = me->_valXtraSize + fill;
|
|
input_offset += fill;
|
|
}
|
|
|
|
// call 'transfer'
|
|
size_t processed = stream->transfer(buffer, bytes);
|
|
if (processed == abort) {
|
|
|
|
me->setError(error_aborted);
|
|
return 0u;
|
|
|
|
} else if (! processed && ! input) {
|
|
|
|
me->setError(error_leftover_input);
|
|
return 0u;
|
|
}
|
|
size_t unprocessed = bytes - processed;
|
|
|
|
// can switch to input buffer, now?
|
|
if (buffer == me->_arrXtra && unprocessed <= input_offset) {
|
|
|
|
me->_valXtraSize = 0u;
|
|
input_offset -= unprocessed;
|
|
|
|
} else { // no? unprocessed data -> extra buffer
|
|
|
|
if (unprocessed > max_read_ahead) {
|
|
|
|
me->setError(error_buffer_overflow);
|
|
return 0;
|
|
}
|
|
me->_valXtraSize = unprocessed;
|
|
memmove(me->_arrXtra, buffer + processed, unprocessed);
|
|
|
|
if (input_offset == size || buffer != me->_arrXtra) {
|
|
|
|
return size;
|
|
}
|
|
}
|
|
} // for
|
|
}
|
|
|
|
#endif /* defined(__hifi__UrlReader__) */
|
|
|