text:
#include <sirikata/core/util/Standard.hh>
#include <sirikata/core/options/Options.hpp>
#include <sirikata/core/transfer/TransferPool.hpp>
#include <sirikata/core/transfer/TransferHandlers.hpp>
#include <sirikata/core/transfer/RemoteFileMetadata.hpp>
#include <sirikata/core/options/Options.hpp>
#include <sirikata/core/options/CommonOptions.hpp>
#include <boost/bind.hpp>
#include <sirikata/core/transfer/MeerkatTransferHandler.hpp>
#include <sirikata/core/transfer/OAuthHttpManager.hpp>
#include <sirikata/core/transfer/URL.hpp>
#include <boost/lexical_cast.hpp>

#include <json_spirit/json_spirit.h>

AUTO_SINGLETON_INSTANCE(Sirikata::Transfer::MeerkatNameHandler);
AUTO_SINGLETON_INSTANCE(Sirikata::Transfer::MeerkatChunkHandler);
AUTO_SINGLETON_INSTANCE(Sirikata::Transfer::MeerkatUploadHandler);

namespace Sirikata {
namespace Transfer {

MeerkatNameHandler& MeerkatNameHandler::getSingleton() {
    return AutoSingleton<MeerkatNameHandler>::getSingleton();
}
void MeerkatNameHandler::destroy() {
    AutoSingleton<MeerkatNameHandler>::destroy();
}

MeerkatChunkHandler& MeerkatChunkHandler::getSingleton() {
    return AutoSingleton<MeerkatChunkHandler>::getSingleton();
}
void MeerkatChunkHandler::destroy() {
    AutoSingleton<MeerkatChunkHandler>::destroy();
}

MeerkatUploadHandler& MeerkatUploadHandler::getSingleton() {
    return AutoSingleton<MeerkatUploadHandler>::getSingleton();
}
void MeerkatUploadHandler::destroy() {
    AutoSingleton<MeerkatUploadHandler>::destroy();
}

MeerkatNameHandler::MeerkatNameHandler()
 : CDN_HOST_NAME(GetOptionValue<String>(OPT_CDN_HOST)),
   CDN_SERVICE(GetOptionValue<String>(OPT_CDN_SERVICE)),
   CDN_DNS_URI_PREFIX(GetOptionValue<String>(OPT_CDN_DNS_URI_PREFIX)),

   mCdnAddr(CDN_HOST_NAME, CDN_SERVICE)
{
}

MeerkatNameHandler::~MeerkatNameHandler() {

}

void MeerkatNameHandler::resolve(std::tr1::shared_ptr<MetadataRequest> request, NameCallback callback) {
    URL url(request->getURI());
    assert(!url.empty());

    std::string dns_uri_prefix = CDN_DNS_URI_PREFIX;
    std::string host_name = CDN_HOST_NAME;
    Network::Address cdn_addr = mCdnAddr;
    if (url.host() != "") {
        host_name = url.context().hostname();
        std::string service = url.context().service();
        if (service == "") {
            service = CDN_SERVICE;
        }
        Network::Address given_addr(host_name, service);
        cdn_addr = given_addr;
    }

    HttpManager::Headers headers;
    headers["Host"] = host_name;

    HttpManager::getSingleton().head(
        cdn_addr, dns_uri_prefix + url.fullpath(),
        std::tr1::bind(&MeerkatNameHandler::request_finished, this, _1, _2, _3, request, callback),
        headers
    );
}

void MeerkatNameHandler::request_finished(std::tr1::shared_ptr<HttpManager::HttpResponse> response,
        HttpManager::ERR_TYPE error, const boost::system::error_code& boost_error,
        std::tr1::shared_ptr<MetadataRequest> request, NameCallback callback) {

    mStats.resolved++;
    mStats.bytesTransferred += (response->getBytesSent() + response->getBytesReceived());

    std::tr1::shared_ptr<RemoteFileMetadata> bad;

    if (error == Transfer::HttpManager::REQUEST_PARSING_FAILED) {
        SILOG(transfer, error, "Request parsing failed during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::RESPONSE_PARSING_FAILED) {
        SILOG(transfer, error, "Response parsing failed during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::BOOST_ERROR) {
        SILOG(transfer, error, "A boost error happened during an HTTP name lookup (" << request->getURI() << "). Boost error = " << boost_error.message());
        callback(bad);
        return;
    } else if (error != HttpManager::SUCCESS) {
        SILOG(transfer, error, "An unknown error happened during an HTTP name lookup. (" << request->getURI() << ")");
        callback(bad);
        return;
    }

    if (response->getHeaders().size() == 0) {
        SILOG(transfer, error, "There were no headers returned during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    }

    HttpManager::Headers::const_iterator it;
    it = response->getHeaders().find("Content-Length");
    if (it != response->getHeaders().end()) {
        SILOG(transfer, error, "Content-Length header was present when it shouldn't be during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    }

    if (response->getStatusCode() != 200) {
        SILOG(transfer, error, "HTTP status code = " << response->getStatusCode() << " instead of 200 during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    }

    it = response->getHeaders().find("File-Size");
    if (it == response->getHeaders().end()) {
        SILOG(transfer, error, "Expected File-Size header not present during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    }
    std::string file_size_str = it->second;

    it = response->getHeaders().find("Hash");
    if (it == response->getHeaders().end()) {
        SILOG(transfer, error, "Expected Hash header not present during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    }
    std::string hash = it->second;

    if (response->getData()) {
        SILOG(transfer, error, "Body present during an HTTP name lookup (" << request->getURI() << ")");
        callback(bad);
        return;
    }

    Fingerprint fp;
    try {
        fp = Fingerprint::convertFromHex(hash);
    } catch(std::invalid_argument e) {
        SILOG(transfer, error, "Hash header didn't contain a valid Fingerprint string (" << request->getURI() << ")");
        callback(bad);
        return;
    }

    std::istringstream istream(file_size_str);
    uint64 file_size;
    istream >> file_size;
    std::ostringstream ostream;
    ostream << file_size;
    if(ostream.str() != file_size_str) {
        SILOG(transfer, error, "Error converting File-Size header string to integer (" << request->getURI() << ")");
        callback(bad);
        return;
    }

    //Just treat everything as a single chunk for now
    Range whole(0, file_size, LENGTH, true);
    Chunk chunk(fp, whole);
    ChunkList chunkList;
    chunkList.push_back(chunk);

    std::tr1::shared_ptr<RemoteFileMetadata> met(new RemoteFileMetadata(fp, request->getURI(),
            file_size, chunkList, response->getRawHeaders()));

    callback(met);
    SILOG(transfer, detailed, "done http name handler request_finished");
}

MeerkatChunkHandler::MeerkatChunkHandler()
 : CDN_HOST_NAME(GetOptionValue<String>(OPT_CDN_HOST)),
   CDN_SERVICE(GetOptionValue<String>(OPT_CDN_SERVICE)),
   CDN_DOWNLOAD_URI_PREFIX(GetOptionValue<String>(OPT_CDN_DOWNLOAD_URI_PREFIX)),
   mCdnAddr(CDN_HOST_NAME, CDN_SERVICE)
{
}

MeerkatChunkHandler::~MeerkatChunkHandler() {
}

void MeerkatChunkHandler::get(std::tr1::shared_ptr<RemoteFileMetadata> file,
        std::tr1::shared_ptr<Chunk> chunk, ChunkCallback callback) {

    //Check for null arguments
    std::tr1::shared_ptr<DenseData> bad;
    if (!file) {
        SILOG(transfer, error, "HttpChunkHandler get called with null file parameter");
        callback(bad);
        return;
    }
    if (!chunk) {
        SILOG(transfer, error, "HttpChunkHandler get called with null chunk parameter");
        callback(bad);
        return;
    }

    //Make sure chunk given is part of file
    bool foundIt = false;
    const ChunkList & chunks = file->getChunkList();
    for (ChunkList::const_iterator it = chunks.begin(); it != chunks.end(); it++) {
        if(*chunk == *it) {
            foundIt = true;
        }
    }
    if(!foundIt) {
        SILOG(transfer, error, "HttpChunkHandler get called with chunk not present in file metadata");
        callback(bad);
        return;
    }

    //Check to see if it's in the cache first
    SharedChunkCache::getSingleton().getCache()->getData(file->getFingerprint(), chunk->getRange(), std::tr1::bind(
            &MeerkatChunkHandler::cache_check_callback, this, _1, file->getURI(), chunk, callback));
}

void MeerkatChunkHandler::get(std::tr1::shared_ptr<Chunk> chunk, ChunkCallback callback) {
    std::tr1::shared_ptr<DenseData> bad;
    if (!chunk) {
        SILOG(transfer, error, "HttpChunkHandler get called with null chunk parameter");
        callback(bad);
        return;
    }
    //Check to see if it's in the cache first
    SharedChunkCache::getSingleton().getCache()->getData(chunk->getHash(), chunk->getRange(), std::tr1::bind(
            &MeerkatChunkHandler::cache_check_callback, this, _1, URI("meerkat:///"), chunk, callback));
}

void MeerkatChunkHandler::cache_check_callback(const SparseData* data, const URI& uri,
            std::tr1::shared_ptr<Chunk> chunk, ChunkCallback callback) {
    if (data) {
        mStats.downloaded++;

        std::tr1::shared_ptr<const DenseData> flattened = data->flatten();
        callback(flattened);
    } else {
        URL url(uri);
        assert(!url.empty());

        std::string download_uri_prefix = CDN_DOWNLOAD_URI_PREFIX;
        std::string host_name = CDN_HOST_NAME;
        Network::Address cdn_addr = mCdnAddr;
        if (url.host() != "") {
            host_name = url.context().hostname();
            std::string service = url.context().service();
            if (service == "") {
                service = CDN_SERVICE;
            }
            Network::Address given_addr(host_name, service);
            cdn_addr = given_addr;
        }

        HttpManager::Headers headers;
        headers["Host"] = host_name;
        headers["Accept-Encoding"] = "deflate, gzip";

        bool chunkReq = false;
        if(!chunk->getRange().goesToEndOfFile() || chunk->getRange().startbyte() != 0) {
            chunkReq = true;
            headers["Range"] = "bytes=" + boost::lexical_cast<String>(chunk->getRange().startbyte()) +
                "-" + boost::lexical_cast<String>(chunk->getRange().endbyte());
        }

        HttpManager::getSingleton().get(
            cdn_addr, download_uri_prefix + "/" + chunk->getHash().convertToHexString(),
            std::tr1::bind(&MeerkatChunkHandler::request_finished, this, _1, _2, _3, uri, chunk, chunkReq, callback),
            headers
        );
    }
}

void MeerkatChunkHandler::request_finished(std::tr1::shared_ptr<HttpManager::HttpResponse> response,
        HttpManager::ERR_TYPE error, const boost::system::error_code& boost_error,
        const URI& uri, std::tr1::shared_ptr<Chunk> chunk,
        bool chunkReq, ChunkCallback callback) {

    mStats.downloaded++;
    mStats.bytesTransferred += (response->getBytesSent() + response->getBytesReceived());

    std::tr1::shared_ptr<DenseData> bad;
    std::string reqType = "file request";
    if(chunkReq) reqType = "chunk request";

    if (error == Transfer::HttpManager::REQUEST_PARSING_FAILED) {
        SILOG(transfer, error, "Request parsing failed during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::RESPONSE_PARSING_FAILED) {
        SILOG(transfer, error, "Response parsing failed during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::BOOST_ERROR) {
        SILOG(transfer, error, "A boost error happened during an HTTP " << reqType << ". Boost error = " << boost_error.message() << " (" << uri << ")");
        callback(bad);
        return;
    } else if (error != HttpManager::SUCCESS) {
        SILOG(transfer, error, "An unknown error happened during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    }

    if (response->getHeaders().size() == 0) {
        SILOG(transfer, error, "There were no headers returned during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    }

    HttpManager::Headers::const_iterator it;
    it = response->getHeaders().find("Content-Length");
    if (it == response->getHeaders().end()) {
        SILOG(transfer, error, "Content-Length header was not present when it should be during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    }

    if (response->getStatusCode() != 200) {
        SILOG(transfer, error, "HTTP status code = " << response->getStatusCode() << " instead of 200 during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    }

    if (!response->getData()) {
        SILOG(transfer, error, "Body not present during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    }

    it = response->getHeaders().find("Content-Range");
    if (chunkReq && it == response->getHeaders().end()) {
        SILOG(transfer, error, "Expected Content-Range header not present during an HTTP " << reqType << " (" << uri << ")");
        callback(bad);
        return;
    } else if (chunkReq) {
        std::string range_str = it->second;
        bool range_parsed = false;

        if (range_str.substr(0,6) == "bytes ") {
            range_str = range_str.substr(6);
            size_type dashPos = range_str.find('-');
            if (dashPos != std::string::npos) {
                range_str.replace(dashPos, 1, " ");

                //total file size is optional
                size_type slashPos = range_str.find('/');
                if (slashPos != std::string::npos) {
                    range_str.replace(slashPos, 1, " ");
                }

                std::istringstream instream(range_str);
                uint64 range_start;
                uint64 range_end;
                instream >> range_start;
                instream >> range_end;

                if (range_start == chunk->getRange().startbyte() &&
                        range_end == chunk->getRange().endbyte() &&
                        response->getData()->length() == chunk->getRange().length()) {
                    range_parsed = true;
                }
            }
        }

        if(!range_parsed) {
            SILOG(transfer, error, "Range header has invalid format during an HTTP " << reqType << " header='" << it->second << "'" << " (" << uri << ")");
            callback(bad);
            return;
        }
    } else if (!chunkReq && response->getData()->length() != chunk->getRange().size()) {
        SILOG(transfer, error, "Data retrieved not expected size during an HTTP "
                << reqType << " response=" << response->getData()->length() << " expected=" << chunk->getRange().size());
        callback(bad);
        return;
    }

    if (chunk->getRange().startbyte() > 0) {
      response->getData()->setBase(chunk->getRange().startbyte());
    }
    SILOG(transfer, detailed, "about to call addToCache with fingerprint ID = " << chunk->getHash().convertToHexString());
    SharedChunkCache::getSingleton().getCache()->addToCache(chunk->getHash(), response->getData());

    SILOG(transfer, detailed, response->getData()->length() << " transferred");

    callback(response->getData());
    SILOG(transfer, detailed, "done http chunk handler request_finished");
}

MeerkatUploadHandler::MeerkatUploadHandler()
 : CDN_HOST_NAME(GetOptionValue<String>(OPT_CDN_HOST)),
   CDN_SERVICE(GetOptionValue<String>(OPT_CDN_SERVICE)),
   CDN_UPLOAD_URI_PREFIX(GetOptionValue<String>(OPT_CDN_UPLOAD_URI_PREFIX)),
   CDN_UPLOAD_STATUS_URI_PREFIX(GetOptionValue<String>(OPT_CDN_UPLOAD_STATUS_URI_PREFIX)),
   mCdnAddr(CDN_HOST_NAME, CDN_SERVICE)
{
}

MeerkatUploadHandler::~MeerkatUploadHandler() {
}

namespace {
// Return true if the service is a default value
bool ServiceIsDefault(const String& s) {
    if (!s.empty() && s != "http" && s != "80")
        return false;
    return true;
}
// Return empty if this is already empty or the default for this
// service. Otherwise, return the old value
String ServiceIfNotDefault(const String& s) {
    if (!ServiceIsDefault(s))
        return s;
    return "80";
}
}

void MeerkatUploadHandler::getServerProps(UploadRequestPtr request, Network::Address& cdn_addr, String& full_oauth_hostinfo) {
    std::string host_name = CDN_HOST_NAME;
    String host_service = ServiceIfNotDefault(CDN_SERVICE);
    cdn_addr = mCdnAddr;

    // FIXME this sort of matches name + chunk approach, but there's not upload
    // URL for a resource, so there's no way to extract all the info we would
    // need. We need some other way of specifying that through the request,
    // i.e. a more generic version of OAuthParams
    String oauth_hostname(request->oauth()->hostname);
    String oauth_service(request->oauth()->service);
    if (oauth_hostname != "") {
        host_name = oauth_hostname;
        host_service = ServiceIfNotDefault(oauth_service);
        Network::Address given_addr(host_name, host_service);
        cdn_addr = given_addr;
    }

    full_oauth_hostinfo = host_name;
    if (!ServiceIsDefault(host_service))
        full_oauth_hostinfo += ":" + host_service;
}

void MeerkatUploadHandler::upload(UploadRequestPtr request, UploadCallback callback) {
    String upload_uri_prefix = CDN_UPLOAD_URI_PREFIX;
    Network::Address cdn_addr = mCdnAddr;
    String full_oauth_hostinfo;
    getServerProps(request, cdn_addr, full_oauth_hostinfo);

    HttpManager::Headers headers;
    headers["Host"] = full_oauth_hostinfo;

    // We handle the regular request parameters in the query string. This isn't
    // ideal, but since some servers sign non-file parameters from multipart
    // form-data (despite the spec saying you only add items to the base
    // signature string if a number of specific conditions are met and they
    // aren't for those fields) we need to keep them somewhere else. This is
    // probably better anyway since it ends up resulting in signing those fields
    // as well.
    //
    // We need to include both the requested path and the user specified args in
    // the query string.
    HttpManager::QueryParameters query_params;
    // Form data consists of parameters
    for(UploadRequest::StringMap::const_iterator it = request->params().begin(); it != request->params().end(); it++) {
        query_params[it->first] = it->second;
    }
    // Requested path
    query_params["path"] = request->path();

    // The multipart data just includes files
    HttpManager::MultipartDataList multipart_post_data;
    for(UploadRequest::StringMap::const_iterator it = request->files().begin(); it != request->files().end(); it++) {
        // Field, value, filename. We assume the field and filename are the same
        multipart_post_data.push_back( HttpManager::MultipartData(it->first, it->second, it->first) );
    }

    OAuthHttpManager oauth_http(request->oauth());
    oauth_http.postMultipartForm(
        cdn_addr, upload_uri_prefix,
        multipart_post_data,
        std::tr1::bind(&MeerkatUploadHandler::request_finished, this, _1, _2, _3, request, callback),
        headers, query_params
    );
}

void MeerkatUploadHandler::request_finished(std::tr1::shared_ptr<HttpManager::HttpResponse> response,
    HttpManager::ERR_TYPE error, const boost::system::error_code& boost_error,
    UploadRequestPtr request, UploadCallback callback) {

    mStats.bytesTransferred += (response->getBytesSent() + response->getBytesReceived());

    Transfer::URI bad;

    if (error == Transfer::HttpManager::REQUEST_PARSING_FAILED) {
        SILOG(transfer, error, "Request parsing failed during an HTTP upload (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::RESPONSE_PARSING_FAILED) {
        SILOG(transfer, error, "Response parsing failed during an HTTP upload (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::BOOST_ERROR) {
        SILOG(transfer, error, "A boost error happened during an HTTP upload (" << request->getIdentifier() << "). Boost error = " << boost_error.message());
        callback(bad);
        return;
    } else if (error != HttpManager::SUCCESS) {
        SILOG(transfer, error, "An unknown error happened during an HTTP upload. (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    if (response->getHeaders().size() == 0) {
        SILOG(transfer, error, "There were no headers returned during an HTTP upload (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    if (response->getStatusCode() != 200) {
        SILOG(transfer, error, "HTTP status code = " << response->getStatusCode() << " instead of 200 during an HTTP upload (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    DenseDataPtr response_data = response->getData();

    // Parse the JSON response
    namespace json = json_spirit;
    json::Value parsed;
    if (!json::read(response_data->asString(), parsed)) {
        SILOG(transfer, error, "Failed to parse upload response as JSON: " << response_data->asString() << " (" << request->getIdentifier() << ")");
        return;
    }

    // Basic success check
    if (!parsed.contains("success") || !parsed.get("success").isBool()) {
        SILOG(transfer, error, "Upload response didn't contain boolean success flag (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }
    if (!parsed.getBool("success")) {
        String reported_error = parsed.getString("error", "(No error message reported)");
        SILOG(transfer, error, "Upload failed: " << reported_error << " (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    // Task ID
    if (!parsed.contains("task_id") || !parsed.get("task_id").isString()) {
        SILOG(transfer, error, "Upload indicated initial success, but couldn't find task (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    String reported_task_id = parsed.getString("task_id");
    SILOG(transfer, detailed, "Upload succeeded, starting to track task id = " << reported_task_id << " (" << request->getIdentifier() << ")");
    // 60 retries * .5s = 30s
    requestStatus(request, reported_task_id, callback, 60);
}

void MeerkatUploadHandler::requestStatus(UploadRequestPtr request, const String& task_id, UploadCallback callback, int32 retries) {
    String upload_status_uri_prefix = CDN_UPLOAD_STATUS_URI_PREFIX;
    Network::Address cdn_addr = mCdnAddr;
    String full_oauth_hostinfo;
    getServerProps(request, cdn_addr, full_oauth_hostinfo);

    HttpManager::Headers headers;
    headers["Host"] = full_oauth_hostinfo;

    HttpManager::QueryParameters query_params;
    query_params["api"] = "true";
    //TODO(ewencp) This is kind of gross -- we need to extract the username
    //parameter because this is currently required by the CDN
    if (request->params().find("username") != request->params().end())
        query_params["username"] = request->params().find("username")->second;

    HttpManager::getSingleton().get(
        cdn_addr, upload_status_uri_prefix + "/" + task_id,
        std::tr1::bind(&MeerkatUploadHandler::handleRequestStatusResult, this, _1, _2, _3, request, task_id, callback, retries),
        headers, query_params
    );
}

void MeerkatUploadHandler::handleRequestStatusResult(
    std::tr1::shared_ptr<HttpManager::HttpResponse> response,
    HttpManager::ERR_TYPE error, const boost::system::error_code& boost_error,
    UploadRequestPtr request, const String& task_id, UploadCallback callback,
    int32 retries
)
{
    mStats.bytesTransferred += (response->getBytesSent() + response->getBytesReceived());

    Transfer::URI bad;

    if (retries <= 0) {
	SILOG(transfer, error, "Failed to upload (" << request->getIdentifier() << ") -- status-check retries exceeded");
        callback(bad);
        return;
    }

    if (error == Transfer::HttpManager::REQUEST_PARSING_FAILED) {
        SILOG(transfer, error, "Request parsing failed during an HTTP upload status check (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::RESPONSE_PARSING_FAILED) {
        SILOG(transfer, error, "Response parsing failed during an HTTP upload status check (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    } else if (error == Transfer::HttpManager::BOOST_ERROR) {
        SILOG(transfer, error, "A boost error happened during an HTTP upload status check (" << request->getIdentifier() << "). Boost error = " << boost_error.message());
        callback(bad);
        return;
    } else if (error != HttpManager::SUCCESS) {
        SILOG(transfer, error, "An unknown error happened during an HTTP upload status check. (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    if (response->getStatusCode() != 200) {
        SILOG(transfer, error, "HTTP status code = " << response->getStatusCode() << " instead of 200 during an HTTP upload status check (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    DenseDataPtr response_data = response->getData();
    // Parse the JSON response
    namespace json = json_spirit;
    json::Value parsed;
    if (!json::read(response_data->asString(), parsed)) {
        SILOG(transfer, error, "Failed to parse upload status check response as JSON: " << response_data->asString() << " (" << request->getIdentifier() << ")");
        return;
    }

    if (!parsed.contains("state") || !parsed.get("state").isString()) {
        SILOG(transfer, error, "No state reported in status check (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    String reported_state = parsed.getString("state");
    if (reported_state != "SUCCESS" && reported_state != "FAILURE") {
        // Some other notice, like PENDING, keep waiting until we get something
        // different
        SILOG(transfer, detailed, "Upload still processing: " << reported_state << " (" << request->getIdentifier() << ")");
        HttpManager::getSingleton().postCallback(
            Duration::seconds(0.5),
            std::tr1::bind(&MeerkatUploadHandler::requestStatus, this, request, task_id, callback, --retries),
            "MeerkatUploadHandler::requestStatus"
        );
        return;
    }

    if (reported_state == "FAILURE") {
        SILOG(transfer, error, "Upload failed during processing (" << request->getIdentifier() << ")");
        callback(bad);
        return;
    }

    String asset_path = "";
    try {
      asset_path = parsed.getString("path");
    }
    catch(std::runtime_error& e) {
      SILOG(transfer, error, "Error uploading (" << request->getIdentifier() <<
	                     ") -- No path returned in upload response");
      callback(bad);
      return;
    }
    SILOG(transfer, detailed, "Upload succeeded and got path " << asset_path << " (" << request->getIdentifier() << ")");
    mStats.uploaded++;

    // Construct the meerkat:// URL for this asset. We need server
    // info to ensure we specify the right server
    Network::Address cdn_addr = mCdnAddr;
    String full_oauth_hostinfo;
    getServerProps(request, cdn_addr, full_oauth_hostinfo);
    // TODO(ewencp) we need the full URI to the model here, maybe
    // automatically selecting optimized?
    String generated_uri_str = "meerkat://" + full_oauth_hostinfo + asset_path;

    Transfer::URI generated_uri(generated_uri_str);
    callback(generated_uri);
    SILOG(transfer, detailed, "done http upload handler request_finished");
}

}
}

max_stars_repo_name: sirikata/sirikata

max_issues_repo_name: sirikata/sirikata

max_forks_repo_name: sirikata/sirikata


text:
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////8
// test_polymorphic.cpp

// (C) Copyright 2002 Robert Ramey - http://www.rrsd.com .
// Use, modification and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

// should pass compilation and execution

#include <cstddef> // NULL
#include <cstdio> // remove
#include <fstream>

#include <boost/config.hpp>
#if defined(BOOST_NO_STDC_NAMESPACE)
namespace std{
    using ::remove;
}
#endif

// the following is to ensure that when one of the libraries changes
// BJAM rebuilds and relinks the test.
/*
#include "polymorphic_text_archive.hpp"
#include "polymorphic_text_warchive.hpp"
#include "polymorphic_binary_archive.hpp"
#include "polymorphic_xml_archive.hpp"
#include "polymorphic_xml_warchive.hpp"
*/

#include <I3Test.h>

#include <archive/polymorphic_oarchive.hpp>
#include <archive/polymorphic_iarchive.hpp>

#include <serialization/nvp.hpp>
#include "test_polymorphic_A.hpp"

template <typename TS /*test settings*/>
void do_test(){
    auto testfile = I3Test::testfile("test_polymorphic");
    const data d;
    data d1;
    // test using using polymorphic interface
    {
        typename TS::test_ostream os(testfile, TS::TEST_STREAM_FLAGS);
        typename TS::test_oarchive oa_implementation(os, TS::TEST_ARCHIVE_FLAGS);
        icecube::archive::polymorphic_oarchive& oa_interface = oa_implementation;
        oa_interface << I3_SERIALIZATION_NVP(d);
    }
    {
        typename TS::test_istream is(testfile, TS::TEST_STREAM_FLAGS);
        typename TS::test_iarchive ia_implementation(is, TS::TEST_ARCHIVE_FLAGS);
        icecube::archive::polymorphic_iarchive& ia_interface = ia_implementation;
        ia_interface >> I3_SERIALIZATION_NVP(d1);
    }
    ENSURE(d == d1);
    std::remove(testfile.c_str());

    // test using using polymorphic implementation.
    {
        typename TS::test_ostream os(testfile, TS::TEST_STREAM_FLAGS);
        typename TS::test_oarchive oa_implementation(os, TS::TEST_ARCHIVE_FLAGS);
        oa_implementation << I3_SERIALIZATION_NVP(d);
    }
    {
        typename TS::test_istream is(testfile, TS::TEST_STREAM_FLAGS);
        typename TS::test_iarchive ia_implementation(is, TS::TEST_ARCHIVE_FLAGS);
        ia_implementation >> I3_SERIALIZATION_NVP(d1);
    }
    ENSURE(d == d1);
    std::remove(testfile.c_str());

    // test using using polymorphic implementation.
    {
        typename TS::test_ostream os(testfile, TS::TEST_STREAM_FLAGS);
        icecube::archive::polymorphic_oarchive* oa_implementation
            = new typename TS::test_oarchive(os, TS::TEST_ARCHIVE_FLAGS);
        *oa_implementation << I3_SERIALIZATION_NVP(d);
        delete oa_implementation;
    }
    {
        typename TS::test_istream is(testfile, TS::TEST_STREAM_FLAGS);
        icecube::archive::polymorphic_iarchive* ia_implementation
            = new typename TS::test_iarchive(is, TS::TEST_ARCHIVE_FLAGS);
        *ia_implementation >> I3_SERIALIZATION_NVP(d1);
        delete ia_implementation;
    }
    ENSURE(d == d1);
    std::remove(testfile.c_str());
}

TEST_GROUP(polymorphic)

#define TEST_SET(name) \
TEST(name ## _polymorphic){ \
    do_test<test_settings>(); \
}

#define I3_ARCHIVE_TEST polymorphic_binary_archive.hpp
#include "select_archive.hpp"
TEST_SET(polymorphic_binary_archive)

#undef I3_ARCHIVE_TEST
#define I3_ARCHIVE_TEST polymorphic_text_archive.hpp
#include "select_archive.hpp"
TEST_SET(polymorphic_text_archive)

#undef I3_ARCHIVE_TEST
#define I3_ARCHIVE_TEST polymorphic_xml_archive.hpp
#include "select_archive.hpp"
TEST_SET(polymorphic_xml_archive)

ext: cpp


text:
#pragma once

#include <iosfwd>
#include <stdarg.h>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/optional.hpp>
#include <boost/noncopyable.hpp>

/* Basic type definitions */

namespace crab {

  // An adaptor for std::ostream that avoids polluting all crab header
  // files with iostream stuff
  class crab_os: boost::noncopyable {

   private:

    static boost::shared_ptr<crab_os> m_cout;
    static boost::shared_ptr<crab_os> m_cerr;

   public:

    static boost::shared_ptr<crab_os> cout();
    static boost::shared_ptr<crab_os> cerr();

   private:

    std::ostream* m_os;

   protected:

    crab_os();

   public:

    crab_os(std::ostream* os);

    virtual ~crab_os();

    virtual crab_os& operator<<(char C);
    virtual crab_os& operator<<(unsigned char C);
    virtual crab_os& operator<<(signed char C);
    virtual crab_os& operator<<(const char *Str);
    virtual crab_os& operator<<(const std::string& Str);
    virtual crab_os& operator<<(unsigned long N);
    virtual crab_os& operator<<(long N);
    virtual crab_os& operator<<(unsigned long long N);
    virtual crab_os& operator<<(long long N);
    virtual crab_os& operator<<(const void *P);
    virtual crab_os& operator<<(unsigned int N);
    virtual crab_os& operator<<(int N);
    virtual crab_os& operator<<(double N);
  };

  extern crab_os& outs();
  extern crab_os& errs();

  // An adaptor for std::ostringstream
  class crab_string_os: public crab_os {

    std::ostringstream* m_string_os;

   public:

    crab_string_os();

    ~crab_string_os();

    std::string str();

    crab_os& operator<<(char C);
    crab_os& operator<<(unsigned char C);
    crab_os& operator<<(signed char C);
    crab_os& operator<<(const char *Str);
    crab_os& operator<<(const std::string& Str);
    crab_os& operator<<(unsigned long N);
    crab_os& operator<<(long N);
    crab_os& operator<<(unsigned long long N);
    crab_os& operator<<(long long N);
    crab_os& operator<<(const void *P);
    crab_os& operator<<(unsigned int N);
    crab_os& operator<<(int N);
    crab_os& operator<<(double N);
  };

}// end namespace

template<typename... ArgTypes>
inline void ___print___(ArgTypes... args)
{
  // trick to expand variadic argument pack without recursion
  using expand_variadic_pack = int[];
  // first zero is to prevent empty braced-init-list
  // void() is to prevent overloaded operator, messing things up
  // trick is to use the side effect of list-initializer to call a function
  // on every argument.
  // (void) is to suppress "statement has no effect" warnings
  (void)expand_variadic_pack{0, ((crab::errs() << args), void(), 0)... };
}

#define CRAB_ERROR(...)              \
  do {                               \
    crab::errs() << "CRAB ERROR: ";  \
    ___print___(__VA_ARGS__);        \
    crab::errs() << "\n";            \
    std::exit (EXIT_FAILURE);        \
  } while (0)

#define CRAB_WARN(...)               \
  do {                               \
    crab::errs() << "CRAB WARNING: ";\
    ___print___(__VA_ARGS__);        \
    crab::errs() << "\n";            \
  } while (0)

namespace crab {

   enum variable_type {
     BOOL_TYPE,
     INT_TYPE,
     REAL_TYPE,
     PTR_TYPE,
     ARR_BOOL_TYPE,
     ARR_INT_TYPE,
     ARR_REAL_TYPE,
     ARR_PTR_TYPE,
     UNK_TYPE};

   inline crab_os& operator<< (crab_os& o, variable_type t)
   {
     switch (t)
     {
       case BOOL_TYPE: o << "bool"; break;
       case INT_TYPE: o << "int"; break;
       case REAL_TYPE: o << "real"; break;
       case PTR_TYPE: o << "ptr"; break;
       case ARR_BOOL_TYPE: o << "arr_bool"; break;
       case ARR_INT_TYPE: o << "arr_int"; break;
       case ARR_REAL_TYPE: o << "arr_real"; break;
       case ARR_PTR_TYPE: o << "arr_ptr"; break;
       default: o << "unknown"; break;
     }
     return o;
   }

   typedef enum {
     BINOP_ADD, BINOP_SUB, BINOP_MUL,
     BINOP_SDIV, BINOP_UDIV, BINOP_SREM, BINOP_UREM,
     BINOP_AND, BINOP_OR, BINOP_XOR, BINOP_SHL, BINOP_LSHR, BINOP_ASHR
   } binary_operation_t;

   typedef enum {
     BINOP_BAND, BINOP_BOR, BINOP_BXOR
   } bool_binary_operation_t;

   typedef enum {
     CAST_TRUNC, CAST_SEXT, CAST_ZEXT
   } cast_operation_t;

   inline crab::crab_os& operator<<(crab::crab_os&o, binary_operation_t op) {
     switch (op) {
       case BINOP_ADD: o << "+"; break;
       case BINOP_SUB: o << "-"; break;
       case BINOP_MUL: o << "*"; break;
       case BINOP_SDIV: o << "/"; break;
       case BINOP_UDIV: o << "/_u"; break;
       case BINOP_SREM: o << "%"; break;
       case BINOP_UREM: o << "%_u"; break;
       case BINOP_AND: o << "&"; break;
       case BINOP_OR: o << "|"; break;
       case BINOP_XOR: o << "^"; break;
       case BINOP_SHL: o << "<<"; break;
       case BINOP_LSHR: o << ">>_l"; break;
       case BINOP_ASHR: o << ">>_r"; break;
       default: CRAB_ERROR("unreachable");
      }
     return o;
   }

   inline crab::crab_os& operator<<(crab::crab_os&o, bool_binary_operation_t op) {
     switch (op) {
       case BINOP_BAND: o << "&"; break;
       case BINOP_BOR: o << "|"; break;
       case BINOP_BXOR: o << "^"; break;
       default: CRAB_ERROR("unreachable");
      }
     return o;
   }

   inline crab::crab_os& operator<<(crab::crab_os&o, cast_operation_t op) {
     switch (op) {
       case CAST_TRUNC: o << "trunc"; break;
       case CAST_SEXT: o << "sext"; break;
       case CAST_ZEXT: o << "zext"; break;
       default: CRAB_ERROR("unreachable");
      }
     return o;
   }

  template<typename T>
  inline boost::optional<T> conv_op (binary_operation_t op);

  template<typename T>
  inline boost::optional<T> conv_op (bool_binary_operation_t op);

  template<typename T>
  inline boost::optional<T> conv_op (cast_operation_t op);

  // toy language for pointer constraints
  typedef enum  { PTR_EQUALITY, PTR_DISEQUALITY } ptr_cst_kind_t;

  template<typename Variable>
  class pointer_constraint {
   public:

    typedef pointer_constraint <Variable> ptr_cst_t;
    typedef boost::optional<Variable> opt_var_t;

   private:

    opt_var_t _lhs;
    opt_var_t _rhs;
    ptr_cst_kind_t _kind;

    /*
       We can only express constraints of the form p {==,!=} q where
       p,q can be either null or a variable.

       !lhs, !rhs, kind == PTR_EQUALITY    -> null == null -> true
       !lhs, !rhs, kind == PTR_DISEQUALITY -> null != null -> false
       !lhs, rhs, kind == PTR_EQUALITY     -> *rhs == null
       lhs, !rhs, kind == PTR_EQUALITY     -> *lhs == null
       !lhs, rhs, kind == PTR_DISEQUALITY  -> *rhs != null
       lhs, !rhs, kind == PTR_DISEQUALITY  -> *lhs != nul
       lhs, rhs, kind == PTR_EQUALITY      -> *lhs == *rhs
       lhs, rhs, kind == PTR_DISEQUALITY   -> *lhs != *rhs
    */

    pointer_constraint (opt_var_t lhs, opt_var_t rhs, ptr_cst_kind_t kind):
        _lhs (lhs), _rhs (rhs), _kind (kind) {
      if (!_lhs && _rhs){ // normalize
        std::swap (_lhs,_rhs);
      }
    }

   public:

    pointer_constraint () : _kind (PTR_EQUALITY) { }

    // return true iff null != null
    bool is_contradiction () const {
      return (!_lhs && !_rhs && _kind == PTR_DISEQUALITY);
    }

    // return true iff null == null
    bool is_tautology () const {
      return (!_lhs && !_rhs && _kind == PTR_EQUALITY);
    }

    bool is_equality () const {
      return (_kind == PTR_EQUALITY);
    }

    bool is_disequality () const {
      return (_kind == PTR_DISEQUALITY);
    }

    // return true iff  p == null or p != null
    bool is_unary () const {
      return (_lhs && !_rhs);
    }

    // return true iff p == q or p != q
    bool is_binary () const {
      return (_lhs && _rhs);
    }

    Variable lhs () const {
      if (!_lhs) CRAB_ERROR ("pointer constraint lhs is null");
      return *_lhs;
    }

    Variable rhs () const {
      if (!_rhs) CRAB_ERROR ("pointer constraint rhs is null");
      return *_rhs;
    }

    static ptr_cst_t mk_true () {
      return ptr_cst_t ();
    }

    static ptr_cst_t mk_false () {
      return ptr_cst_t (opt_var_t (), opt_var_t (), PTR_DISEQUALITY);
    }

    static ptr_cst_t mk_eq_null (Variable v)  {
      return ptr_cst_t (opt_var_t (v), opt_var_t (), PTR_EQUALITY);
    }

    static ptr_cst_t mk_diseq_null (Variable v) {
      return ptr_cst_t (opt_var_t (v), opt_var_t (), PTR_DISEQUALITY);
    }

    static ptr_cst_t mk_eq (Variable v1, Variable v2)  {
      return ptr_cst_t (opt_var_t (v1), opt_var_t (v2), PTR_EQUALITY);
    }

    static ptr_cst_t mk_diseq (Variable v1, Variable v2)  {
      return ptr_cst_t (opt_var_t (v1), opt_var_t (v2), PTR_DISEQUALITY);
    }

    void write (crab::crab_os& o) const {
      if (is_contradiction () ) {
        o << "false";
      } else if (is_tautology ()) {
        o << "true";
      } else {
        assert (_lhs);

        o << lhs ();

        if (_kind == PTR_EQUALITY)
          o << " == ";
        else
          o << " != ";

        if (!_rhs)
          o << "NULL";
        else
          o <<  rhs ();
      }
    }
  };

  inline crab::crab_os& operator<<(crab::crab_os& o, const ptr_cst_kind_t &k) {
    if (k == PTR_EQUALITY )
      o << " == ";
    else
      o << " != ";
    return o;
  }

  template<typename Variable>
  inline crab::crab_os& operator<<(crab::crab_os& o,
                                   const pointer_constraint<Variable> &cst) {
    cst.write (o);
    return o;
  }

} // end namespace

namespace ikos {
  // Numerical type for indexed objects
  typedef uint64_t index_t;

  // Interface for writeable objects
  class writeable {
   public:
    virtual void write(crab::crab_os& o) = 0;
    virtual ~writeable() {}
  }; // class writeable

  inline crab::crab_os& operator<<(crab::crab_os& o, writeable& x) {
    x.write(o);
    return o;
  }

  // Container for typed variables used by the crab abstract domains
  // and linear_constraints.
  template< typename Number, typename VariableName >
  class variable {
    // XXX: template parameter Number is required even if the class
    // does not use it.  This allows, e.g., linear_constraint to
    // deduce the kind of Number from constraints like x < y.

   public:

    typedef variable< Number, VariableName > variable_t;
    typedef typename VariableName::index_t index_t;
    typedef unsigned bitwidth_t;
    typedef crab::variable_type type_t;

  private:

    VariableName _n;
    type_t _type;
    bitwidth_t _width;

   public:

    /**
     * DO NOT USE this constructor to create a CFG since all CFG
     * statements must be strongly typed.  This constructor is
     * intended to be used only abstract domains to generate temporary
     * variables.
     **/
    explicit variable(const VariableName& n)
      : _n(n), _type(crab::UNK_TYPE), _width(0) { }

   public:

    variable(const VariableName& n, type_t type)
      : _n(n), _type(type), _width(0) { }

    variable(const VariableName& n, type_t type, bitwidth_t width)
      : _n(n), _type(type), _width(width) { }

    variable(const variable_t& o)
      : _n(o._n), _type(o._type), _width(o._width) { }

    variable(variable_t &&o)
      : _n(std::move(o._n)), _type(std::move(o._type)), _width(std::move(o._width)) { }

    variable_t& operator=(const variable_t &o) {
      if (this != &o) {
        _n = o._n;
	_type = o._type;
	_width = o._width;
      }
      return *this;
    }

    bool is_typed () const { return _type != crab::UNK_TYPE; }

    bool is_array_type() const {
      return is_typed() && _type >= crab::ARR_BOOL_TYPE;
    }

    bool is_int_type() const {
      return _type == crab::INT_TYPE;
    }

    bool is_bool_type() const {
      return _type == crab::BOOL_TYPE;
    }

    bool is_ptr_type() const {
      return _type == crab::PTR_TYPE;
    }

    type_t get_type() const { return _type;}

    bool has_bitwidth() const { return _width > 0; }

    bitwidth_t get_bitwidth() const { return _width;}

    const VariableName& name() const { return _n; }

    // Cannot be const because from VariableName we might want to
    // access to its variable factory to create e.g., new
    // VariableName's.
    VariableName& name() { return _n;}

    index_t index() const { return _n.index(); }

    bool operator==(const variable_t& o) const {
      return _n.index () == o._n.index ();
    }

    bool operator!=(const variable_t& o) const {
      return (!(operator==(o)));
    }

    bool operator<(const variable_t& o) const {
      return _n.index () < o._n.index ();
    }

    void write(crab::crab_os& o) const {
      o << _n;
    }

  }; // class variable

  template< typename Number, typename VariableName >
  class variable_ref {
   public:

    typedef variable< Number, VariableName > variable_t;
    typedef typename variable_t::index_t index_t;
    typedef typename variable_t::bitwidth_t bitwidth_t;
    typedef typename variable_t::type_t type_t;
    typedef variable_ref<Number, VariableName> variable_ref_t;

  private:

    boost::shared_ptr<variable_t> m_v;

   public:

    variable_ref(): m_v(nullptr) {}

    variable_ref(variable_t v): m_v(boost::make_shared<variable_t>(v)) { }

    bool is_null() const {
      return !m_v;
    }

    variable_t get() const {
      assert(!is_null());
      return *m_v;
    }

    bool is_typed () const {
      assert(!is_null());
      return m_v->is_typed();
    }

    bool is_array_type() const {
      assert(!is_null());
      return m_v->is_array_type();
    }

    bool is_int_type() const {
      assert(!is_null());
      return m_v->is_int_type();
    }

    bool is_bool_type() const {
      assert(!is_null());
      return m_v->is_bool_type();
    }

    bool is_ptr_type() const {
      assert(!is_null());
      return m_v->is_ptr_type();
    }

    type_t get_type() const {
      assert(!is_null());
      return m_v->get_type();
    }

    bool has_bitwidth() const {
      assert(!is_null());
      return m_v->has_bitwidth();
    }

    bitwidth_t get_bitwidth() const {
      assert(!is_null());
      return m_v->get_bitwidth();
    }

    const VariableName& name() const {
      assert(!is_null());
      return m_v->name();
    }

    VariableName& name() {
      assert(!is_null());
      return m_v->name();
    }

    index_t index() const {
      assert(!is_null());
      return m_v->index();
    }

    bool operator==(const variable_ref_t& o) const {
      assert(!is_null());
      return m_v->operator==(o);
    }

    bool operator!=(const variable_ref_t& o) const {
      assert(!is_null());
      return m_v->operator!=(o);
    }

    bool operator<(const variable_ref_t& o) const {
      assert(!is_null());
      return m_v->operator<(o);
    }

    void write(crab::crab_os& o) const {
      return m_v->write(o);
    }
  }; // class variable_ref

  template< typename Number, typename VariableName >
  inline index_t hash_value (const variable<Number, VariableName> &v) {
    return v.index ();
  }

  template< typename Number, typename VariableName >
  inline index_t hash_value (const variable_ref<Number, VariableName> &v) {
    return v.index ();
  }

  template< typename Number, typename VariableName >
  inline crab::crab_os& operator<<(crab::crab_os& o, const variable<Number, VariableName> &v) {
    v.write(o);
    return o;
  }

  template< typename Number, typename VariableName >
  inline crab::crab_os& operator<<(crab::crab_os& o, const variable_ref<Number, VariableName> &v) {
    v.write(o);
    return o;
  }

} // end namespace

namespace crab {
  namespace domains {
    using namespace ikos;
  }
}

ext: hpp

max_stars_repo_name: aziem/crab

max_issues_repo_name: aziem/crab

max_forks_repo_name: aziem/crab


text:
// Copyright Ricardo Calheiros de Miranda Cosme 2017.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

#pragma once

#include "observable/traits.hpp"
#include "observable/types.hpp"

#include <boost/signals2.hpp>
#include <boost/iterator.hpp>
#include <boost/iterator/reverse_iterator.hpp>
#include <memory>
#include <unordered_map>

namespace observable {

template<typename Observed_>
struct map;

template<typename Observed>
struct observable_of<
    Observed,
    typename std::enable_if<is_map<Observed>::value>::type
>
{
    using type = map<Observed>;
};

template<typename Observed>
class map_iterator
    : public boost::iterator_adaptor<
        map_iterator<Observed>,
        typename Observed::Observed::iterator,
        std::pair<
            typename Observed::Observed::key_type,
            std::shared_ptr<typename Observed::reference>
        >,
        boost::bidirectional_traversal_tag,
        std::pair<
            typename Observed::Observed::key_type,
            std::shared_ptr<typename Observed::reference>
            >,
        typename Observed::Observed::difference_type
    >
{
public:
    map_iterator() = default;

    explicit map_iterator
    (Observed& observed, const typename Observed::Observed::iterator& it)
        : map_iterator::iterator_adaptor_(it)
        , _observed(&observed)
    {}
private:
    friend class boost::iterator_core_access;
    using value = std::pair<
        typename Observed::Observed::key_type,
        std::shared_ptr<typename Observed::reference>
        >;

    value dereference() const
    {
        auto it = this->base_reference();
        //TODO:lvalue ref?
        auto& p = *it;
        return std::make_pair(p.first, _observed->get_reference(it));
    }
    Observed* _observed{nullptr};
};

template<typename Observed_>
struct map
{
    using Observed = Observed_;

    using key_type = typename Observed::key_type;
    using mapped_type = typename Observed::mapped_type;
    using value_type = typename Observed::value_type;
    using key_compare = typename Observed::key_compare;
    using reference = observable_of_t<typename Observed::mapped_type>;
    using const_reference = typename Observed::const_reference;
    using pointer = reference*;
    using const_pointer = typename Observed::const_pointer;
    using iterator = map_iterator<map<Observed>>;
    using reverse_iterator = boost::reverse_iterator<iterator>;
    using const_iterator = typename Observed::const_iterator;
    using const_reverse_iterator = typename Observed::const_reverse_iterator;
    using size_type = typename Observed::size_type;
    using difference_type = typename Observed::difference_type;
    using allocator_type = typename Observed::allocator_type;

    map() = default;

    map(Observed& observed)
        : _observed(&observed)
    {}

    iterator begin() noexcept
    { return iterator(*this, _observed->begin()); }

    reverse_iterator rbegin() noexcept
    {
        return reverse_iterator
            (iterator(*this, _observed->end()));
    }

    const_iterator cbegin() const noexcept
    { return _observed->cbegin(); }

    const_reverse_iterator crbegin() noexcept
    { return _observed->crbegin(); }

    iterator end() noexcept
    { return iterator(*this, _observed->end()); }

    reverse_iterator rend() noexcept
    {
        return reverse_iterator
            (iterator(*this, _observed->begin()));
    }

    const_iterator cend() const noexcept
    { return _observed->cend(); }

    const_reverse_iterator crend() noexcept
    { return _observed->crend(); }

    bool empty() const noexcept
    { return _observed->empty(); }

    size_type size() const noexcept
    { return _observed->size(); }

    size_type max_size() const noexcept
    { return _observed->max_size(); }

    std::shared_ptr<reference> at(const key_type& key)
    {
        auto it = _observed->lower_bound(key);
        if (it == _observed->end() || _observed->key_comp()(key, (*it).first))
            throw std::out_of_range("map::at");
        return get_reference(it);
    }

    const mapped_type&
    at(const key_type& key) const
    { return _observed->at(key); }

    std::shared_ptr<reference> operator[](const key_type& key)
    {
        auto it = _observed->lower_bound(key);
        if (it == _observed->end() || _observed->key_comp()(key, (*it).first))
            it = _observed->insert(it, value_type
                               (key, mapped_type()));
        return get_reference(it);
    }

    std::shared_ptr<reference> operator[](key_type&& key)
    {
        auto it = _observed->lower_bound(key);
        if (it == _observed->end() || _observed->key_comp()(key, (*it).first))
            it = _observed->insert(it, value_type
                               (std::move(key), mapped_type()));
        return get_reference(it);
    }

    void clear() noexcept
    {
        _before_erase(*_observed, _observed->cend());
        _observed->clear();
        _on_erase(*_observed, const_iterator{});
        _on_change(*_observed);
    }

    iterator erase(const_iterator pos)
    {
        _before_erase(*_observed, pos);
        auto it = _observed->erase(pos);
        _on_erase(*_observed, it);
        _on_change(*_observed);
        return iterator(*this, it);
    }

    iterator erase(const_iterator first,
                   const_iterator last)
    {
        _before_erase(*_observed, first);
        auto it = _observed->erase(first, last);
        _on_erase(*_observed, it);
        _on_change(*_observed);
        return iterator(*this, it);
    }

    size_type erase(const key_type& key)
    {
        auto rng = _observed->equal_range(key);
        auto before_size = _observed->size();
        _before_erase(*_observed, rng.first);
        erase(rng.first, rng.second);
        auto n = before_size - _observed->size();
        if (n > 0)
        {
            _on_erase(*_observed, rng.second);
            _on_change(*_observed);
        }
        return n;
    }

    template<typename... Args>
    std::pair<iterator, bool> emplace(Args&&... args)
    {
        auto ret = _observed->emplace(std::forward<Args>(args)...);
        if (ret.second)
        {
            _on_insert(*_observed, ret.first);
            _on_change(*_observed);
        }
        return std::make_pair(iterator(*this, ret.first), ret.second);
    }

    template<typename... Args>
    iterator emplace_hint
    (const_iterator hint, Args&&... args)
    {
        auto before_size = _observed->size();
        auto it = _observed->emplace_hint(hint, std::forward<Args>(args)...);
        if (_observed->size() != before_size)
        {
            _on_insert(*_observed, it);
            _on_change(*_observed);
        }
        return iterator(*this, it);
    }

    std::pair<iterator, bool> insert
    (const value_type& value)
    {
        auto ret = _observed->insert(value);
        if (ret.second)
        {
            _on_insert(*_observed, ret.first);
            _on_change(*_observed);
        }
        return std::make_pair(iterator(*this, ret.first), ret.second);
    }

    std::pair<iterator, bool> insert(value_type&& value)
    {
        auto ret = _observed->insert(std::move(value));
        if (ret.second)
        {
            _on_insert(*_observed, ret.first);
            _on_change(*_observed);
        }
        return std::make_pair(iterator(*this, ret.first), ret.second);
    }

    iterator insert(const_iterator hint,
                    const value_type& value)
    {
        auto before_size = _observed->size();
        auto it = _observed->insert(hint, value);
        if (_observed->size() != before_size)
        {
            _on_insert(*_observed, it);
            _on_change(*_observed);
        }
        return iterator(*this, it);
    }

    template<typename InputIt>
    void insert(InputIt first, InputIt last)
    {
        auto before_size = _observed->size();
        _observed->insert(first, last);
        if (_observed->size() != before_size)
        {
            _on_insert(*_observed, const_iterator{}); //TODO
            _on_change(*_observed);
        }
    }

    void insert(std::initializer_list<value_type> ilist)
    { insert(ilist.begin(), ilist.end()); }

    void swap(Observed& other)
    {
        _before_erase(*_observed, _observed->cend());
        _observed->swap(other);
        //TODO: check?
        _on_insert(*_observed, const_iterator{});
        _on_erase(*_observed, const_iterator{});
        _on_change(*_observed);
    }

    size_type count
    (const key_type& key) const noexcept
    { return _observed->count(key); }

    iterator find(const key_type& key)
    { return iterator(*this, _observed->find(key)); }

    const_iterator find
    (const key_type& key) const
    { return _observed->find(key); }

    std::pair<iterator, iterator> equal_range
    (const key_type& key)
    {
        auto p = _observed->equal_range(key);
        return std::make_pair(iterator(*this, p.first),
                              iterator(*this, p.second));
    }

    std::pair<const_iterator,
              const_iterator> equal_range
    (const key_type& key) const
    { return _observed->equal_range(key); }

    template<typename F>
    boost::signals2::connection before_erase(F&& f)
    { return _before_erase.connect(std::forward<F>(f)); }

    template<typename F>
    boost::signals2::connection on_erase(F&& f)
    { return _on_erase.connect(std::forward<F>(f)); }

    template<typename F>
    boost::signals2::connection on_insert(F&& f)
    { return _on_insert.connect(std::forward<F>(f)); }

    template<typename F>
    boost::signals2::connection on_change(F&& f)
    { return _on_change.connect(std::forward<F>(f)); }

    template<typename F>
    boost::signals2::connection on_value_change(F&& f)
    { return _on_value_change.connect(std::forward<F>(f)); }

    const Observed& get() const noexcept
    { return *_observed; }

    Observed* _observed;

    boost::signals2::signal<void(const Observed&, const_iterator)>
    _on_erase, _on_insert, _on_value_change, _before_erase;

    boost::signals2::signal<void(const Observed&)> _on_change;

    std::unordered_map<const_pointer,
                       std::weak_ptr<reference>> _it2observable;
private:
    std::shared_ptr<reference> get_reference(typename Observed::iterator it)
    {
        auto observable = _it2observable[&*it].lock();
        if (!observable)
        {
            auto& it2observable = _it2observable;
            auto it_ptr = &*it;
            observable = std::shared_ptr<reference>
                (new reference(observable_factory(it->second)),
                 [&it2observable, it_ptr](reference *p)
                 {
                     it2observable.erase(it_ptr);
                     delete p;
                 });
            auto& container = *this;
            observable->_on_change.connect(
                [&container, it](const typename reference::Observed&)
                {
                    container._on_value_change(container.get(), it);
                    container._on_change(container.get());
                });
            it2observable[&*it] = observable;
        }
        return observable;
    }
    friend class map_iterator<map<Observed>>;
};

}

max_stars_repo_name: ricardocosme/observable

max_issues_repo_name: ricardocosme/observable

max_forks_repo_name: ricardocosme/observable