<commit_before>#include "DynamicData.hpp"
#include "ProcessObject.hpp"

namespace fast {

DynamicData::~DynamicData() {
    delete fillCount;
    delete emptyCount;

#if defined(__APPLE__) || defined(__MACOSX)
    std::string name = "FAST_fill_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
    boost::interprocess::named_semaphore::remove(name.c_str());
    name = "FAST_empty_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
    boost::interprocess::named_semaphore::remove(name.c_str());
#endif
}

void DynamicData::setMaximumNumberOfFrames(uint nrOfFrames) {
    mMaximumNrOfFrames = nrOfFrames;
    if(mFrames2.size() > 0)
        throw Exception("Must call setMaximumNumberOfFrames before streaming is started");
    if(mMaximumNrOfFrames > 0) {
        delete fillCount;
        delete emptyCount;

        // Use named semaphore if Mac
#if defined(__APPLE__) || defined(__MACOSX)
        std::string name = "FAST_fill_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
        boost::interprocess::named_semaphore::remove(name.c_str());
        std::string name2 = "FAST_empty_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
        boost::interprocess::named_semaphore::remove(name2.c_str());
        fillCount = new boost::interprocess::named_semaphore(boost::interprocess::create_only, name.c_str(), 0);
        emptyCount = new boost::interprocess::named_semaphore(boost::interprocess::create_only, name2.c_str(), nrOfFrames);
#else
        fillCount = new boost::interprocess::interprocess_semaphore(0);
        emptyCount = new boost::interprocess::interprocess_semaphore(nrOfFrames);
#endif
    }
}

uint DynamicData::getLowestFrameCount() const {
    if(mConsumerFrameCounters.size() == 0)
        return 0;

    boost::unordered_map<WeakPointer<Object>, uint>::const_iterator it;
    uint lowestFrameCount = std::numeric_limits<uint>::max();
    for(it = mConsumerFrameCounters.begin(); it != mConsumerFrameCounters.end(); it++) {
        if(it->second < lowestFrameCount) {
            lowestFrameCount = it->second;
        }
    }

    return lowestFrameCount;
}

void DynamicData::removeOldFrames(uint frameCounter) {
    // TODO this could be implemented in a faster way
    typename boost::unordered_map<uint, DataObject::pointer>::iterator it = mFrames2.begin();
    while(it != mFrames2.end()) {
        if(it->first < frameCounter) {
            it = mFrames2.erase(it);
		} else {
			it++;
		}
    }
}

void DynamicData::registerConsumer(Object::pointer processObject) {
    registerConsumer(WeakPointer<Object>(processObject));
}

void DynamicData::registerConsumer(WeakPointer<Object> processObject) {
    Streamer::pointer streamer = getStreamer();
    mStreamMutex.lock();
     if(mConsumerFrameCounters.count(processObject) == 0) {
        if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
            mConsumerFrameCounters[processObject] = mCurrentFrameCounter;
        } else if(streamer->getStreamingMode() == STREAMING_MODE_STORE_ALL_FRAMES) {
            mConsumerFrameCounters[processObject] = 0;
        } else {
            mConsumerFrameCounters[processObject] = getLowestFrameCount();
        }
     }
    mStreamMutex.unlock();
}

DataObject::pointer DynamicData::getNextFrame(Object::pointer processObject) {
    return getNextFrame(WeakPointer<Object>(processObject));
}

void DynamicData::setAllConsumersUpToDate() {
    unsigned long timestamp = getTimestamp();
    boost::unordered_map<WeakPointer<Object>, uint>::iterator it;
    for(it = mConsumerFrameCounters.begin(); it != mConsumerFrameCounters.end(); it++) {
        ProcessObject::pointer consumer = ProcessObject::pointer(it->first.lock());
        consumer->updateTimestamp(mPtr.lock());
    }
}

DataObject::pointer DynamicData::getNextFrame(WeakPointer<Object> processObject) {
    Streamer::pointer streamer = getStreamer();
    if(!streamer.isValid()) {
        // If streamer has been deleted, return the current frame instead
        throw Exception("Streamer has been deleted, but someone has called getNextFrame");
        //return getCurrentFrame();
    }

    // Producer consumer model
    if(mMaximumNrOfFrames > 0 && streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        bool frameShouldBeRemoved = true;

        if(mConsumerFrameCounters.count(processObject) > 0) {
            uint thisFrameCounter = mConsumerFrameCounters[processObject]; // Current consumer frame counter

            // If any other frame counters are less or equal, we do not want to remove frame
            boost::unordered_map<WeakPointer<Object>, uint>::const_iterator it;
            for(it = mConsumerFrameCounters.begin(); it != mConsumerFrameCounters.end(); it++) {
                if(it->second <= thisFrameCounter) {
                    frameShouldBeRemoved = false;
                    break;
                }
            }
        }

        if(frameShouldBeRemoved)
            fillCount->wait(); // decrement
    }

    mStreamMutex.lock();
    if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
        // Always return last frame
        if(mCurrentFrameCounter == 0) {
            mStreamMutex.unlock();
            throw Exception("Trying to get next frame, when no frame has ever been given to dynamic data.");
        }
        DataObject::pointer returnData = mCurrentFrame2;
        mStreamMutex.unlock();
        return mCurrentFrame2;
    }
    // If process object is not registered, register it
    if(mConsumerFrameCounters.count(processObject) == 0) {
        if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
            mConsumerFrameCounters[processObject] = mCurrentFrameCounter;
        } else if(streamer->getStreamingMode() == STREAMING_MODE_STORE_ALL_FRAMES) {
            mConsumerFrameCounters[processObject] = 0;
        } else {
            uint lowestFrameCount = getLowestFrameCount(); // This must be assigned to a variable first to ensure correct result!
            mConsumerFrameCounters[processObject] = lowestFrameCount;
        }
    }
    // Return frame
    DataObject::pointer returnData;
    if(mFrames2.count(mConsumerFrameCounters[processObject]) > 0) {
        returnData = mFrames2[mConsumerFrameCounters[processObject]];
    } else {
        mStreamMutex.unlock();
        throw Exception("Frame in dynamic data was not found");
    }

    // Increment
    mConsumerFrameCounters[processObject] = mConsumerFrameCounters[processObject]+1;

    // If PROCESS_ALL and this has smallest frame counter, remove old frame
    if(streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        removeOldFrames(getLowestFrameCount());
        if(mFrames2.size() > 0) { // Update timestamp if there are more frames available
            updateModifiedTimestamp();
        } else {
            // All frames are gone, make sure timestamps that all POs have are up to date
            setAllConsumersUpToDate();
        }
    } else if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
        // With newest frame only, always remove?
        //mFrames2.erase(removeFrame);
    } else {
        // Update timestamp if there are more frames available
        if(mConsumerFrameCounters[processObject] < mFrames2.size()) {
            updateModifiedTimestamp();
        }
    }

    mStreamMutex.unlock();
    // Producer consumer
    if(mMaximumNrOfFrames > 0 && streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        emptyCount->post(); // increment
    }

    return returnData;
}

DataObject::pointer DynamicData::getCurrentFrame() {
    Streamer::pointer streamer = getStreamer();
    mStreamMutex.lock();
    /*
    if(mFrames.size() == 0 || mFrames.size() <= mCurrentFrame) {
        if(!streamer.isValid()) {
            mStreamMutex.unlock();
            throw Exception("Streamer does not exist (anymore), stop.");
        } else {
            if(streamer->hasReachedEnd()) {
                mStreamMutex.unlock();
                throw Exception("Streamer has reached the end.");
            } else {
                mStreamMutex.unlock();
                throw Exception("This exception should not have occured. ");
            }
        }
    }
    */
    DataObject::pointer ret = mCurrentFrame2;
    mStreamMutex.unlock();
    return ret;
}

void DynamicData::addFrame(DataObject::pointer frame) {
    Streamer::pointer streamer = getStreamer();
    if(!streamer.isValid()) {
        //throw Exception("A DynamicImage must have a streamer set before it can be used.");
        return;
    }
    if(mMaximumNrOfFrames > 0) {
        if(streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
            // Producer consumer model using semaphores
            emptyCount->wait(); // decrement
            //down(mutex);
        } else if(streamer->getStreamingMode() == STREAMING_MODE_STORE_ALL_FRAMES) {
            if(mFrames2.size() >= mMaximumNrOfFrames)
                throw NoMoreFramesException("Maximum number of frames reached. You can change the this number using the setMaximumNumberOfFrames method on the streamer/dynamic data objects.");
        }
    }
    mStreamMutex.lock();

    updateModifiedTimestamp();
    if(getStreamer()->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
        mFrames2.clear();
    }
    mFrames2[mCurrentFrameCounter] = frame;
    mCurrentFrame2 = frame;
    mCurrentFrameCounter++;
    mStreamMutex.unlock();
    if(mMaximumNrOfFrames > 0 && streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        fillCount->post(); // increment
    }
}

DynamicData::DynamicData() {
    mCurrentFrameCounter = 0;
    mMaximumNrOfFrames = 0;
    mIsDynamicData = true;
    mHasReachedEnd = false;
    fillCount = NULL;
    emptyCount = NULL;
    updateModifiedTimestamp();

#if defined(__APPLE__) || defined(__MACOSX)
    static uint namedSemaphoresCount = 0;
    mSemaphoreNumber = namedSemaphoresCount;
    namedSemaphoresCount++;
#endif
}

unsigned int DynamicData::getSize() const {
    return mFrames2.size();
}

bool DynamicData::hasReachedEnd() {
    Streamer::pointer streamer = getStreamer();
    if(!streamer.isValid()) {
        throw Exception("A DynamicData must have a streamer set before it can be used.");
    }
    mStreamMutex.lock();
    // Check if has reached end can be changed to true
    if(!mHasReachedEnd) {
        // TODO the checks for NEWEST_FRAME AND PROCESS_ALL are not necessarily correct, for a pipeline with several steps
        switch(streamer->getStreamingMode()) {
        case STREAMING_MODE_NEWEST_FRAME_ONLY:
            // Should get frame nr of last to see if we have read the last
            if(streamer->hasReachedEnd())
                mHasReachedEnd = true;
            break;
        case STREAMING_MODE_PROCESS_ALL_FRAMES:
            if(streamer->hasReachedEnd() && mFrames2.size() == 0)
                mHasReachedEnd = true;
            break;
        case STREAMING_MODE_STORE_ALL_FRAMES:
            if(streamer->hasReachedEnd() && streamer->getNrOfFrames() == getLowestFrameCount())
                mHasReachedEnd = true;
            break;
        }
    }
    mStreamMutex.unlock();
    return mHasReachedEnd;
}

}
<commit_msg>fixed a bug in dynamic data getnextframe<commit_after>#include "DynamicData.hpp"
#include "ProcessObject.hpp"

namespace fast {

DynamicData::~DynamicData() {
    delete fillCount;
    delete emptyCount;

#if defined(__APPLE__) || defined(__MACOSX)
    std::string name = "FAST_fill_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
    boost::interprocess::named_semaphore::remove(name.c_str());
    name = "FAST_empty_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
    boost::interprocess::named_semaphore::remove(name.c_str());
#endif
}

void DynamicData::setMaximumNumberOfFrames(uint nrOfFrames) {
    mMaximumNrOfFrames = nrOfFrames;
    if(mFrames2.size() > 0)
        throw Exception("Must call setMaximumNumberOfFrames before streaming is started");
    if(mMaximumNrOfFrames > 0) {
        delete fillCount;
        delete emptyCount;

        // Use named semaphore if Mac
#if defined(__APPLE__) || defined(__MACOSX)
        std::string name = "FAST_fill_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
        boost::interprocess::named_semaphore::remove(name.c_str());
        std::string name2 = "FAST_empty_count_" + boost::lexical_cast<std::string>(mSemaphoreNumber);
        boost::interprocess::named_semaphore::remove(name2.c_str());
        fillCount = new boost::interprocess::named_semaphore(boost::interprocess::create_only, name.c_str(), 0);
        emptyCount = new boost::interprocess::named_semaphore(boost::interprocess::create_only, name2.c_str(), nrOfFrames);
#else
        fillCount = new boost::interprocess::interprocess_semaphore(0);
        emptyCount = new boost::interprocess::interprocess_semaphore(nrOfFrames);
#endif
    }
}

uint DynamicData::getLowestFrameCount() const {
    if(mConsumerFrameCounters.size() == 0)
        return 0;

    boost::unordered_map<WeakPointer<Object>, uint>::const_iterator it;
    uint lowestFrameCount = std::numeric_limits<uint>::max();
    for(it = mConsumerFrameCounters.begin(); it != mConsumerFrameCounters.end(); it++) {
        if(it->second < lowestFrameCount) {
            lowestFrameCount = it->second;
        }
    }

    return lowestFrameCount;
}

void DynamicData::removeOldFrames(uint frameCounter) {
    // TODO this could be implemented in a faster way
    typename boost::unordered_map<uint, DataObject::pointer>::iterator it = mFrames2.begin();
    while(it != mFrames2.end()) {
        if(it->first < frameCounter) {
            it = mFrames2.erase(it);
		} else {
			it++;
		}
    }
}

void DynamicData::registerConsumer(Object::pointer processObject) {
    registerConsumer(WeakPointer<Object>(processObject));
}

void DynamicData::registerConsumer(WeakPointer<Object> processObject) {
    Streamer::pointer streamer = getStreamer();
    mStreamMutex.lock();
     if(mConsumerFrameCounters.count(processObject) == 0) {
        if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
            mConsumerFrameCounters[processObject] = mCurrentFrameCounter;
        } else if(streamer->getStreamingMode() == STREAMING_MODE_STORE_ALL_FRAMES) {
            mConsumerFrameCounters[processObject] = 0;
        } else {
            mConsumerFrameCounters[processObject] = getLowestFrameCount();
        }
     }
    mStreamMutex.unlock();
}

DataObject::pointer DynamicData::getNextFrame(Object::pointer processObject) {
    return getNextFrame(WeakPointer<Object>(processObject));
}

void DynamicData::setAllConsumersUpToDate() {
    unsigned long timestamp = getTimestamp();
    boost::unordered_map<WeakPointer<Object>, uint>::iterator it;
    for(it = mConsumerFrameCounters.begin(); it != mConsumerFrameCounters.end(); it++) {
        ProcessObject::pointer consumer = ProcessObject::pointer(it->first.lock());
        consumer->updateTimestamp(mPtr.lock());
    }
}

DataObject::pointer DynamicData::getNextFrame(WeakPointer<Object> processObject) {
    Streamer::pointer streamer = getStreamer();
    if(!streamer.isValid()) {
        // If streamer has been deleted, return the current frame instead
        throw Exception("Streamer has been deleted, but someone has called getNextFrame");
        //return getCurrentFrame();
    }

    // Producer consumer model
    if(mMaximumNrOfFrames > 0 && streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        bool frameShouldBeRemoved = true;

        if(mConsumerFrameCounters.count(processObject) > 0) {
            uint thisFrameCounter = mConsumerFrameCounters[processObject]; // Current consumer frame counter

            // If any other frame counters are less or equal, we do not want to remove frame
            boost::unordered_map<WeakPointer<Object>, uint>::const_iterator it;
            for(it = mConsumerFrameCounters.begin(); it != mConsumerFrameCounters.end(); it++) {
                if(it->second <= thisFrameCounter) {
                    frameShouldBeRemoved = false;
                    break;
                }
            }
        }

        if(frameShouldBeRemoved)
            fillCount->wait(); // decrement
    }

    mStreamMutex.lock();
    if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
        // Always return last frame
        if(mCurrentFrameCounter == 0) {
            mStreamMutex.unlock();
            throw Exception("Trying to get next frame, when no frame has ever been given to dynamic data.");
        }
        DataObject::pointer returnData = mCurrentFrame2;
        mStreamMutex.unlock();
        return mCurrentFrame2;
    }
    // If process object is not registered, register it
    if(mConsumerFrameCounters.count(processObject) == 0) {
        if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
            mConsumerFrameCounters[processObject] = mCurrentFrameCounter;
        } else if(streamer->getStreamingMode() == STREAMING_MODE_STORE_ALL_FRAMES) {
            mConsumerFrameCounters[processObject] = 0;
        } else {
            uint lowestFrameCount = getLowestFrameCount(); // This must be assigned to a variable first to ensure correct result!
            mConsumerFrameCounters[processObject] = lowestFrameCount;
        }
    }
    // Return frame
    DataObject::pointer returnData;
    if(mFrames2.count(mConsumerFrameCounters[processObject]) > 0) {
        returnData = mFrames2[mConsumerFrameCounters[processObject]];
    } else {
        mStreamMutex.unlock();
        throw Exception("Frame in dynamic data was not found");
    }

    // Increment
    mConsumerFrameCounters[processObject] = mConsumerFrameCounters[processObject]+1;

    // If PROCESS_ALL and this has smallest frame counter, remove old frame
    if(streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        removeOldFrames(getLowestFrameCount());
        if(mFrames2.size() > 0) { // Update timestamp if there are more frames available
            updateModifiedTimestamp();
            // For each consumer
            boost::unordered_map<WeakPointer<Object>, uint>::iterator it;
            for(it = mConsumerFrameCounters.begin(); it != mConsumerFrameCounters.end(); it++) {
                // Check if next frame is available
                if(mFrames2.count(it->second) == 0) {
                    // Set consumer up to date, so that it will not request data yet
                    ProcessObject::pointer consumer = it->first.lock();
                    consumer->updateTimestamp(mPtr.lock());
                }
            }
        } else {
            // All frames are gone, make sure timestamps that all POs have are up to date
            setAllConsumersUpToDate();
        }
    } else if(streamer->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
        // With newest frame only, always remove?
        //mFrames2.erase(removeFrame);
    } else {
        // Update timestamp if there are more frames available
        if(mConsumerFrameCounters[processObject] < mFrames2.size()) {
            updateModifiedTimestamp();
        }
    }

    mStreamMutex.unlock();
    // Producer consumer
    if(mMaximumNrOfFrames > 0 && streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        emptyCount->post(); // increment
    }

    return returnData;
}

DataObject::pointer DynamicData::getCurrentFrame() {
    Streamer::pointer streamer = getStreamer();
    mStreamMutex.lock();
    /*
    if(mFrames.size() == 0 || mFrames.size() <= mCurrentFrame) {
        if(!streamer.isValid()) {
            mStreamMutex.unlock();
            throw Exception("Streamer does not exist (anymore), stop.");
        } else {
            if(streamer->hasReachedEnd()) {
                mStreamMutex.unlock();
                throw Exception("Streamer has reached the end.");
            } else {
                mStreamMutex.unlock();
                throw Exception("This exception should not have occured. ");
            }
        }
    }
    */
    DataObject::pointer ret = mCurrentFrame2;
    mStreamMutex.unlock();
    return ret;
}

void DynamicData::addFrame(DataObject::pointer frame) {
    Streamer::pointer streamer = getStreamer();
    if(!streamer.isValid()) {
        //throw Exception("A DynamicImage must have a streamer set before it can be used.");
        return;
    }
    if(mMaximumNrOfFrames > 0) {
        if(streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
            // Producer consumer model using semaphores
            emptyCount->wait(); // decrement
            //down(mutex);
        } else if(streamer->getStreamingMode() == STREAMING_MODE_STORE_ALL_FRAMES) {
            if(mFrames2.size() >= mMaximumNrOfFrames)
                throw NoMoreFramesException("Maximum number of frames reached. You can change the this number using the setMaximumNumberOfFrames method on the streamer/dynamic data objects.");
        }
    }
    mStreamMutex.lock();

    updateModifiedTimestamp();
    if(getStreamer()->getStreamingMode() == STREAMING_MODE_NEWEST_FRAME_ONLY) {
        mFrames2.clear();
    }
    mFrames2[mCurrentFrameCounter] = frame;
    mCurrentFrame2 = frame;
    mCurrentFrameCounter++;
    mStreamMutex.unlock();
    if(mMaximumNrOfFrames > 0 && streamer->getStreamingMode() == STREAMING_MODE_PROCESS_ALL_FRAMES) {
        fillCount->post(); // increment
    }
}

DynamicData::DynamicData() {
    mCurrentFrameCounter = 0;
    mMaximumNrOfFrames = 0;
    mIsDynamicData = true;
    mHasReachedEnd = false;
    fillCount = NULL;
    emptyCount = NULL;
    updateModifiedTimestamp();

#if defined(__APPLE__) || defined(__MACOSX)
    static uint namedSemaphoresCount = 0;
    mSemaphoreNumber = namedSemaphoresCount;
    namedSemaphoresCount++;
#endif
}

unsigned int DynamicData::getSize() const {
    return mFrames2.size();
}

bool DynamicData::hasReachedEnd() {
    Streamer::pointer streamer = getStreamer();
    if(!streamer.isValid()) {
        throw Exception("A DynamicData must have a streamer set before it can be used.");
    }
    mStreamMutex.lock();
    // Check if has reached end can be changed to true
    if(!mHasReachedEnd) {
        // TODO the checks for NEWEST_FRAME AND PROCESS_ALL are not necessarily correct, for a pipeline with several steps
        switch(streamer->getStreamingMode()) {
        case STREAMING_MODE_NEWEST_FRAME_ONLY:
            // Should get frame nr of last to see if we have read the last
            if(streamer->hasReachedEnd())
                mHasReachedEnd = true;
            break;
        case STREAMING_MODE_PROCESS_ALL_FRAMES:
            if(streamer->hasReachedEnd() && mFrames2.size() == 0)
                mHasReachedEnd = true;
            break;
        case STREAMING_MODE_STORE_ALL_FRAMES:
            if(streamer->hasReachedEnd() && streamer->getNrOfFrames() == getLowestFrameCount())
                mHasReachedEnd = true;
            break;
        }
    }
    mStreamMutex.unlock();
    return mHasReachedEnd;
}

}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "shared/shmem.h"

#include <fcntl.h>
#include <linux/memfd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include <atomic>
#include <filesystem>
#include <string>

#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"

#ifndef F_LINUX_SPECIFIC_BASE
#define F_LINUX_SPECIFIC_BASE 1024
#endif
#ifndef F_ADD_SEALS
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#endif
#ifndef F_SEAL_SEAL
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
#endif
#ifndef F_SEAL_SHRINK
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
#endif
#ifndef F_SEAL_GROW
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#endif

namespace fs = std::filesystem;

namespace ghost {

constexpr size_t kHugepageSize = 2 * 1024 * 1024;
static const char* kMemFdPrefix = "ghost-shmem-";

#define MFD_GOOGLE_SPECIFIC_BASE 0x0200U
#define MFD_HUGEPAGE (MFD_GOOGLE_SPECIFIC_BASE << 0)

// Please don't use "0" as a header version, it's not distinguishable from
// an uninitialized header.
static constexpr int64_t kHeaderVersion = 1;

// This currently occupies the first page of every mapping (from offset zero).
struct GhostShmem::InternalHeader {
  int64_t header_version;

  size_t mapping_size;
  size_t header_size;
  size_t client_size;

  std::atomic<bool> ready, finished;

  int owning_pid;
  int64_t client_version;
};

GhostShmem::GhostShmem(int64_t client_version, const char* name, size_t size) {
  CreateShmem(client_version, name, size);
}

bool GhostShmem::Attach(int64_t client_version, const char* name, pid_t pid) {
  return ConnectShmem(client_version, name, pid);
}

GhostShmem::~GhostShmem() {
  if (hdr_) {
    hdr_->finished.store(true);
  }
  if (shmem_) {
    munmap(shmem_, map_size_);
  }
  if (memfd_ >= 0) {
    close(memfd_);
  }
}

void GhostShmem::MarkReady() { hdr_->ready.store(true); }

void GhostShmem::WaitForReady() {
  // TODO: Use a shared futex here.
  while (!hdr_->ready.load()) {
  }
}

size_t GhostShmem::size() {
  // We apply internal adjustments, e.g. our header, hugepages, etc.
  return hdr_->client_size;
}

void GhostShmem::CreateShmem(int64_t client_version, const char* suffix,
                             size_t size) {
  int MFD_FLAGS = MFD_CLOEXEC | MFD_ALLOW_SEALING;
  const int MFD_SEALS = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
  std::string name;

  // Suffixes must currently be unique for the hosting process.
  CHECK_EQ(OpenGhostShmemFd(suffix, Gtid::Current().tid()), -1);

  name = kMemFdPrefix;
  name.append(suffix);
  memfd_ = memfd_create(name.c_str(), MFD_FLAGS);
  CHECK_GE(memfd_, 0);

  // Prepend our header to the mapping.
  map_size_ = roundup2(size + kHeaderReservedBytes, kHugepageSize);
  CHECK_LE(map_size_, UINT32_MAX);
  CHECK_ZERO(ftruncate(memfd_, map_size_));
  CHECK_ZERO(fcntl(memfd_, F_ADD_SEALS, MFD_SEALS));

  shmem_ =
      mmap(nullptr, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_, 0);
  CHECK_NE(shmem_, MAP_FAILED);

  // At this point the shmem_ is created, our header is initialized, but the
  // region is not yet ready.  Clients must call MarkReady() before we'll allow
  // connections against it to proceed.
  hdr_ = static_cast<InternalHeader*>(shmem_);
  char* bytes = static_cast<char*>(shmem_);
  data_ = bytes + kHeaderReservedBytes;

  // We can safely initialize InternalHeader data fields after this point, as
  // MarkReady() cannot yet proceed.
  hdr_->header_version = kHeaderVersion;
  hdr_->mapping_size = map_size_;
  hdr_->client_size = map_size_ - kHeaderReservedBytes;
  hdr_->header_size = kHeaderReservedBytes;
  hdr_->owning_pid = getpid();  // Should probably be process.
}

bool GhostShmem::ConnectShmem(int64_t client_version, const char* suffix,
                              pid_t pid) {
  memfd_ = OpenGhostShmemFd(suffix, pid);
  if (memfd_ < 0) {
    return false;
  }

  struct stat sb;
  CHECK_ZERO(fstat(memfd_, &sb));

  map_size_ = sb.st_size;
  shmem_ =
      mmap(nullptr, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_, 0);
  CHECK_NE(shmem_, MAP_FAILED);

  // Avoid deadlock between agent and the task it is scheduling. This happens
  // if both tasks (agent and non-agent) fault on the same page in shared mem
  // concurrently. Subsequently when the page is ready then it is possible that
  // the non-agent task is woken up first but doesn't get a chance to run
  // because the agent (that is responsible for scheduling it) is also blocked
  // on the same page.
  //
  // See b/173811264 for details.
  CHECK_ZERO(mlock(shmem_, map_size_));

  // Setup internal fields.
  hdr_ = static_cast<InternalHeader*>(shmem_);
  char* bytes = static_cast<char*>(shmem_);
  data_ = bytes + kHeaderReservedBytes;

  // Ensure we synchronize on the remote side marking that content is ready
  // before trying to validate.
  WaitForReady();

  CHECK_EQ(hdr_->header_version, kHeaderVersion);
  CHECK_EQ(hdr_->client_version, client_version);
  CHECK_EQ(hdr_->mapping_size, map_size_);
  CHECK_EQ(hdr_->header_size, kHeaderReservedBytes);
  return true;
}

// static
int GhostShmem::OpenGhostShmemFd(const char* suffix, pid_t pid) {
  std::string path = "/proc/" + std::to_string(pid) + "/fd";
  std::string needle("/memfd:");
  needle.append(kMemFdPrefix);
  needle.append(suffix);

  for (auto& f : fs::directory_iterator(path)) {
    CHECK(fs::is_symlink(f));
    std::string p = fs::read_symlink(f);
    if (absl::StartsWith(p, needle)) {
      std::string path = fs::path(f);
      int fd = open(path.c_str(), O_RDWR | O_CLOEXEC);
      CHECK_GE(fd, 0);
      return fd;
    }
  }
  return -1;
}

// static
GhostShmem* GhostShmem::GetShmemBlob(size_t size) {
  static std::atomic<int> unique = 0;
  std::string blob = absl::StrCat(
      "blob-", std::to_string(unique.fetch_add(1, std::memory_order_relaxed)));
  // GhostShmem needs a unique name per process for the memfd
  ghost::GhostShmem* shmem =
      new ghost::GhostShmem(/* client_version = */ 0, blob.data(), size);
  shmem->MarkReady();

  return shmem;
}

}  // namespace ghost
<commit_msg>cl/428811065: ghost: handle race with /proc/PID/fd/<commit_after>// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "shared/shmem.h"

#include <fcntl.h>
#include <linux/memfd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include <atomic>
#include <filesystem>
#include <string>

#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"

#ifndef F_LINUX_SPECIFIC_BASE
#define F_LINUX_SPECIFIC_BASE 1024
#endif
#ifndef F_ADD_SEALS
#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9)
#endif
#ifndef F_SEAL_SEAL
#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */
#endif
#ifndef F_SEAL_SHRINK
#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */
#endif
#ifndef F_SEAL_GROW
#define F_SEAL_GROW 0x0004 /* prevent file from growing */
#endif

namespace fs = std::filesystem;

namespace ghost {

constexpr size_t kHugepageSize = 2 * 1024 * 1024;
static const char* kMemFdPrefix = "ghost-shmem-";

#define MFD_GOOGLE_SPECIFIC_BASE 0x0200U
#define MFD_HUGEPAGE (MFD_GOOGLE_SPECIFIC_BASE << 0)

// Please don't use "0" as a header version, it's not distinguishable from
// an uninitialized header.
static constexpr int64_t kHeaderVersion = 1;

// This currently occupies the first page of every mapping (from offset zero).
struct GhostShmem::InternalHeader {
  int64_t header_version;

  size_t mapping_size;
  size_t header_size;
  size_t client_size;

  std::atomic<bool> ready, finished;

  int owning_pid;
  int64_t client_version;
};

GhostShmem::GhostShmem(int64_t client_version, const char* name, size_t size) {
  CreateShmem(client_version, name, size);
}

bool GhostShmem::Attach(int64_t client_version, const char* name, pid_t pid) {
  return ConnectShmem(client_version, name, pid);
}

GhostShmem::~GhostShmem() {
  if (hdr_) {
    hdr_->finished.store(true);
  }
  if (shmem_) {
    munmap(shmem_, map_size_);
  }
  if (memfd_ >= 0) {
    close(memfd_);
  }
}

void GhostShmem::MarkReady() { hdr_->ready.store(true); }

void GhostShmem::WaitForReady() {
  // TODO: Use a shared futex here.
  while (!hdr_->ready.load()) {
  }
}

size_t GhostShmem::size() {
  // We apply internal adjustments, e.g. our header, hugepages, etc.
  return hdr_->client_size;
}

void GhostShmem::CreateShmem(int64_t client_version, const char* suffix,
                             size_t size) {
  int MFD_FLAGS = MFD_CLOEXEC | MFD_ALLOW_SEALING;
  const int MFD_SEALS = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL;
  std::string name;

  // Suffixes must currently be unique for the hosting process.
  CHECK_EQ(OpenGhostShmemFd(suffix, Gtid::Current().tid()), -1);

  name = kMemFdPrefix;
  name.append(suffix);
  memfd_ = memfd_create(name.c_str(), MFD_FLAGS);
  CHECK_GE(memfd_, 0);

  // Prepend our header to the mapping.
  map_size_ = roundup2(size + kHeaderReservedBytes, kHugepageSize);
  CHECK_LE(map_size_, UINT32_MAX);
  CHECK_ZERO(ftruncate(memfd_, map_size_));
  CHECK_ZERO(fcntl(memfd_, F_ADD_SEALS, MFD_SEALS));

  shmem_ =
      mmap(nullptr, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_, 0);
  CHECK_NE(shmem_, MAP_FAILED);

  // At this point the shmem_ is created, our header is initialized, but the
  // region is not yet ready.  Clients must call MarkReady() before we'll allow
  // connections against it to proceed.
  hdr_ = static_cast<InternalHeader*>(shmem_);
  char* bytes = static_cast<char*>(shmem_);
  data_ = bytes + kHeaderReservedBytes;

  // We can safely initialize InternalHeader data fields after this point, as
  // MarkReady() cannot yet proceed.
  hdr_->header_version = kHeaderVersion;
  hdr_->mapping_size = map_size_;
  hdr_->client_size = map_size_ - kHeaderReservedBytes;
  hdr_->header_size = kHeaderReservedBytes;
  hdr_->owning_pid = getpid();  // Should probably be process.
}

bool GhostShmem::ConnectShmem(int64_t client_version, const char* suffix,
                              pid_t pid) {
  memfd_ = OpenGhostShmemFd(suffix, pid);
  if (memfd_ < 0) {
    return false;
  }

  struct stat sb;
  CHECK_ZERO(fstat(memfd_, &sb));

  map_size_ = sb.st_size;
  shmem_ =
      mmap(nullptr, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED, memfd_, 0);
  CHECK_NE(shmem_, MAP_FAILED);

  // Avoid deadlock between agent and the task it is scheduling. This happens
  // if both tasks (agent and non-agent) fault on the same page in shared mem
  // concurrently. Subsequently when the page is ready then it is possible that
  // the non-agent task is woken up first but doesn't get a chance to run
  // because the agent (that is responsible for scheduling it) is also blocked
  // on the same page.
  //
  // See b/173811264 for details.
  CHECK_ZERO(mlock(shmem_, map_size_));

  // Setup internal fields.
  hdr_ = static_cast<InternalHeader*>(shmem_);
  char* bytes = static_cast<char*>(shmem_);
  data_ = bytes + kHeaderReservedBytes;

  // Ensure we synchronize on the remote side marking that content is ready
  // before trying to validate.
  WaitForReady();

  CHECK_EQ(hdr_->header_version, kHeaderVersion);
  CHECK_EQ(hdr_->client_version, client_version);
  CHECK_EQ(hdr_->mapping_size, map_size_);
  CHECK_EQ(hdr_->header_size, kHeaderReservedBytes);
  return true;
}

// static
int GhostShmem::OpenGhostShmemFd(const char* suffix, pid_t pid) {
  std::string path = "/proc/" + std::to_string(pid) + "/fd";
  std::string needle("/memfd:");
  needle.append(kMemFdPrefix);
  needle.append(suffix);

  for (auto& f : fs::directory_iterator(path)) {
    // It's possible for f to disappear at any moment if the file is closed.
    std::error_code ec;
    std::string p = fs::read_symlink(f, ec);
    if (ec) {
      continue;
    }
    if (absl::StartsWith(p, needle)) {
      std::string path = fs::path(f);
      int fd = open(path.c_str(), O_RDWR | O_CLOEXEC);
      if (fd < 0) {
        continue;
      }
      return fd;
    }
  }
  return -1;
}

// static
GhostShmem* GhostShmem::GetShmemBlob(size_t size) {
  static std::atomic<int> unique = 0;
  std::string blob = absl::StrCat(
      "blob-", std::to_string(unique.fetch_add(1, std::memory_order_relaxed)));
  // GhostShmem needs a unique name per process for the memfd
  ghost::GhostShmem* shmem =
      new ghost::GhostShmem(/* client_version = */ 0, blob.data(), size);
  shmem->MarkReady();

  return shmem;
}

}  // namespace ghost
<|endoftext|>


<commit_before>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Author: sligocki@google.com (Shawn Ligocki)

#include "net/instaweb/http/public/content_type.h"

#include "net/instaweb/util/public/basictypes.h"
#include "net/instaweb/util/public/string_util.h"

namespace net_instaweb {

namespace {

const ContentType kTypes[] = {
  // Canonical types:
  {"text/html",                     ".html",  ContentType::kHtml},  // RFC 2854
  {"application/xhtml+xml",         ".xhtml", ContentType::kXhtml},  // RFC 3236
  {"application/ce-html+xml",       ".xhtml", ContentType::kCeHtml},

  {"text/javascript",               ".js",   ContentType::kJavascript},
  {"text/css",                      ".css",  ContentType::kCss},
  {"text/plain",                    ".txt",  ContentType::kText},
  {"text/xml",                      ".xml",  ContentType::kXml},  // RFC 3023
  {"image/png",                     ".png",  ContentType::kPng},
  {"image/gif",                     ".gif",  ContentType::kGif},
  {"image/jpeg",                    ".jpg",  ContentType::kJpeg},
  {"application/x-shockwave-flash", ".swf",  ContentType::kSwf},
  {"image/webp",                    ".webp", ContentType::kWebp},
  {"application/json",              ".json", ContentType::kJson},
  {"application/pdf",               ".pdf",  ContentType::kPdf},  // RFC 3778

  // Synonyms; Note that the canonical types are referenced by index
  // in the named references declared below.
  {"application/x-javascript", ".js",   ContentType::kJavascript},
  {"application/javascript",   ".js",   ContentType::kJavascript},
  {"text/ecmascript",          ".js",   ContentType::kJavascript},
  {"text/x-js",                ".js",   ContentType::kJavascript},
  {"application/ecmascript",   ".js",   ContentType::kJavascript},
  {"image/jpeg",               ".jpeg", ContentType::kJpeg},
  {"text/html",                ".htm",  ContentType::kHtml},
  {"application/xml",          ".xml",  ContentType::kXml},  // RFC 3023
};
const int kNumTypes = arraysize(kTypes);

}  // namespace

const ContentType& kContentTypeHtml = kTypes[0];
const ContentType& kContentTypeXhtml = kTypes[1];
const ContentType& kContentTypeCeHtml = kTypes[2];

const ContentType& kContentTypeJavascript = kTypes[3];
const ContentType& kContentTypeCss = kTypes[4];
const ContentType& kContentTypeText = kTypes[5];
const ContentType& kContentTypeXml = kTypes[6];

const ContentType& kContentTypePng = kTypes[7];
const ContentType& kContentTypeGif = kTypes[8];
const ContentType& kContentTypeJpeg = kTypes[9];
const ContentType& kContentTypeSwf = kTypes[10];
const ContentType& kContentTypeWebp = kTypes[11];
const ContentType& kContentTypeJson = kTypes[12];

const ContentType& kContentTypePdf = kTypes[13];

int ContentType::MaxProducedExtensionLength() {
  return 4;  // .jpeg or .webp
}

bool ContentType::IsHtmlLike() const {
  switch (type_) {
    case kHtml:
    case kXhtml:
    case kCeHtml:
      return true;
    default:
      return false;
  }
}

bool ContentType::IsXmlLike() const {
  switch (type_) {
    case kXhtml:
    case kXml:
      return true;
    default:
      return false;
  }
}

bool ContentType::IsFlash() const {
  switch (type_) {
    case kSwf:
      return true;
    default:
      return false;
  }
}

bool ContentType::IsImage() const {
  switch (type_) {
    case kPng:
    case kGif:
    case kJpeg:
    case kWebp:
      return true;
    default:
      return false;
  }
}

const ContentType* NameExtensionToContentType(const StringPiece& name) {
  // Get the name from the extension.
  StringPiece::size_type ext_pos = name.rfind('.');
  const ContentType* res = NULL;
  if (ext_pos != StringPiece::npos) {
    StringPiece ext = name.substr(ext_pos);
    // TODO(jmarantz): convert to a map if the list gets large.
    for (int i = 0; i < kNumTypes; ++i) {
      if (StringCaseEqual(ext, kTypes[i].file_extension())) {
        res = &kTypes[i];
        break;
      }
    }
  }
  return res;
}

const ContentType* MimeTypeToContentType(const StringPiece& mime_type) {
  // TODO(jmarantz): convert to a map if the list gets large.
  const ContentType* res = NULL;

  // The content-type can have a "; charset=...".  We are not interested
  // in that, for the purpose of our ContentType object.
  //
  // TODO(jmarantz): we should be grabbing the encoding, however, and
  // saving it so that when we emit content-type headers for resources,
  // they include the proper encoding.
  StringPiece stripped_mime_type;
  StringPiece::size_type semi_colon = mime_type.find(';');
  if (semi_colon == StringPiece::npos) {
    stripped_mime_type = mime_type;
  } else {
    stripped_mime_type = mime_type.substr(0, semi_colon);
  }

  for (int i = 0; i < kNumTypes; ++i) {
    if (StringCaseEqual(stripped_mime_type, kTypes[i].mime_type())) {
      res = &kTypes[i];
      break;
    }
  }
  return res;
}

// TODO(nforman): Have some further indication of whether
// content_type_str was just empty or invalid.
bool ParseContentType(const StringPiece& content_type_str,
                      GoogleString* mime_type,
                      GoogleString* charset) {
  StringPiece content_type = content_type_str;
  // Set default values
  mime_type->clear();
  charset->clear();

  if (content_type.empty()) {
    return false;
  }

  // Mime type is in the form: "\w+/\w+ *;(.*;)* *charset *= *\w+"
  StringPieceVector semi_split;
  SplitStringPieceToVector(content_type, ";", &semi_split, false);
  if (semi_split.size() == 0) {
    return false;
  }
  semi_split[0].CopyToString(mime_type);
  for (int i = 1, n = semi_split.size(); i < n; ++i) {
    StringPieceVector eq_split;
    SplitStringPieceToVector(semi_split[i], "=", &eq_split, false);
    if (eq_split.size() == 2) {
      TrimWhitespace(&eq_split[0]);
      if (StringCaseEqual(eq_split[0], "charset")) {
        TrimWhitespace(&eq_split[1]);
        eq_split[1].CopyToString(charset);
        break;
      }
    }
  }

  return !mime_type->empty() || !charset->empty();
}

}  // namespace net_instaweb
<commit_msg>Change default content type of javascript to application/javascript rather than text/javascript to avoid confusing certain firewalls.<commit_after>/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Author: sligocki@google.com (Shawn Ligocki)

#include "net/instaweb/http/public/content_type.h"

#include "net/instaweb/util/public/basictypes.h"
#include "net/instaweb/util/public/string_util.h"

namespace net_instaweb {

namespace {

const ContentType kTypes[] = {
  // Canonical types:
  {"text/html",                     ".html",  ContentType::kHtml},  // RFC 2854
  {"application/xhtml+xml",         ".xhtml", ContentType::kXhtml},  // RFC 3236
  {"application/ce-html+xml",       ".xhtml", ContentType::kCeHtml},

  {"application/javascript",        ".js",   ContentType::kJavascript},
     // RFC 4329 makes this canonical; text/... can break firewall gzipping
  {"text/css",                      ".css",  ContentType::kCss},
  {"text/plain",                    ".txt",  ContentType::kText},
  {"text/xml",                      ".xml",  ContentType::kXml},  // RFC 3023
  {"image/png",                     ".png",  ContentType::kPng},
  {"image/gif",                     ".gif",  ContentType::kGif},
  {"image/jpeg",                    ".jpg",  ContentType::kJpeg},
  {"application/x-shockwave-flash", ".swf",  ContentType::kSwf},
  {"image/webp",                    ".webp", ContentType::kWebp},
  {"application/json",              ".json", ContentType::kJson},
  {"application/pdf",               ".pdf",  ContentType::kPdf},  // RFC 3778

  // Synonyms; Note that the canonical types are referenced by index
  // in the named references declared below.
  {"application/x-javascript", ".js",   ContentType::kJavascript},
  {"text/javascript",          ".js",   ContentType::kJavascript},
  {"text/ecmascript",          ".js",   ContentType::kJavascript},
  {"text/x-js",                ".js",   ContentType::kJavascript},
  {"application/ecmascript",   ".js",   ContentType::kJavascript},
  {"image/jpeg",               ".jpeg", ContentType::kJpeg},
  {"text/html",                ".htm",  ContentType::kHtml},
  {"application/xml",          ".xml",  ContentType::kXml},  // RFC 3023
};
const int kNumTypes = arraysize(kTypes);

}  // namespace

const ContentType& kContentTypeHtml = kTypes[0];
const ContentType& kContentTypeXhtml = kTypes[1];
const ContentType& kContentTypeCeHtml = kTypes[2];

const ContentType& kContentTypeJavascript = kTypes[3];
const ContentType& kContentTypeCss = kTypes[4];
const ContentType& kContentTypeText = kTypes[5];
const ContentType& kContentTypeXml = kTypes[6];

const ContentType& kContentTypePng = kTypes[7];
const ContentType& kContentTypeGif = kTypes[8];
const ContentType& kContentTypeJpeg = kTypes[9];
const ContentType& kContentTypeSwf = kTypes[10];
const ContentType& kContentTypeWebp = kTypes[11];
const ContentType& kContentTypeJson = kTypes[12];

const ContentType& kContentTypePdf = kTypes[13];

int ContentType::MaxProducedExtensionLength() {
  return 4;  // .jpeg or .webp
}

bool ContentType::IsHtmlLike() const {
  switch (type_) {
    case kHtml:
    case kXhtml:
    case kCeHtml:
      return true;
    default:
      return false;
  }
}

bool ContentType::IsXmlLike() const {
  switch (type_) {
    case kXhtml:
    case kXml:
      return true;
    default:
      return false;
  }
}

bool ContentType::IsFlash() const {
  switch (type_) {
    case kSwf:
      return true;
    default:
      return false;
  }
}

bool ContentType::IsImage() const {
  switch (type_) {
    case kPng:
    case kGif:
    case kJpeg:
    case kWebp:
      return true;
    default:
      return false;
  }
}

const ContentType* NameExtensionToContentType(const StringPiece& name) {
  // Get the name from the extension.
  StringPiece::size_type ext_pos = name.rfind('.');
  const ContentType* res = NULL;
  if (ext_pos != StringPiece::npos) {
    StringPiece ext = name.substr(ext_pos);
    // TODO(jmarantz): convert to a map if the list gets large.
    for (int i = 0; i < kNumTypes; ++i) {
      if (StringCaseEqual(ext, kTypes[i].file_extension())) {
        res = &kTypes[i];
        break;
      }
    }
  }
  return res;
}

const ContentType* MimeTypeToContentType(const StringPiece& mime_type) {
  // TODO(jmarantz): convert to a map if the list gets large.
  const ContentType* res = NULL;

  // The content-type can have a "; charset=...".  We are not interested
  // in that, for the purpose of our ContentType object.
  //
  // TODO(jmarantz): we should be grabbing the encoding, however, and
  // saving it so that when we emit content-type headers for resources,
  // they include the proper encoding.
  StringPiece stripped_mime_type;
  StringPiece::size_type semi_colon = mime_type.find(';');
  if (semi_colon == StringPiece::npos) {
    stripped_mime_type = mime_type;
  } else {
    stripped_mime_type = mime_type.substr(0, semi_colon);
  }

  for (int i = 0; i < kNumTypes; ++i) {
    if (StringCaseEqual(stripped_mime_type, kTypes[i].mime_type())) {
      res = &kTypes[i];
      break;
    }
  }
  return res;
}

// TODO(nforman): Have some further indication of whether
// content_type_str was just empty or invalid.
bool ParseContentType(const StringPiece& content_type_str,
                      GoogleString* mime_type,
                      GoogleString* charset) {
  StringPiece content_type = content_type_str;
  // Set default values
  mime_type->clear();
  charset->clear();

  if (content_type.empty()) {
    return false;
  }

  // Mime type is in the form: "\w+/\w+ *;(.*;)* *charset *= *\w+"
  StringPieceVector semi_split;
  SplitStringPieceToVector(content_type, ";", &semi_split, false);
  if (semi_split.size() == 0) {
    return false;
  }
  semi_split[0].CopyToString(mime_type);
  for (int i = 1, n = semi_split.size(); i < n; ++i) {
    StringPieceVector eq_split;
    SplitStringPieceToVector(semi_split[i], "=", &eq_split, false);
    if (eq_split.size() == 2) {
      TrimWhitespace(&eq_split[0]);
      if (StringCaseEqual(eq_split[0], "charset")) {
        TrimWhitespace(&eq_split[1]);
        eq_split[1].CopyToString(charset);
        break;
      }
    }
  }

  return !mime_type->empty() || !charset->empty();
}

}  // namespace net_instaweb
<|endoftext|>


<commit_before>//===-- tsan_clock.cc -----------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
#include "tsan_clock.h"
#include "tsan_rtl.h"

// It's possible to optimize clock operations for some important cases
// so that they are O(1). The cases include singletons, once's, local mutexes.
// First, SyncClock must be re-implemented to allow indexing by tid.
// It must not necessarily be a full vector clock, though. For example it may
// be a multi-level table.
// Then, each slot in SyncClock must contain a dirty bit (it's united with
// the clock value, so no space increase). The acquire algorithm looks
// as follows:
// void acquire(thr, tid, thr_clock, sync_clock) {
//   if (!sync_clock[tid].dirty)
//     return;  // No new info to acquire.
//              // This handles constant reads of singleton pointers and
//              // stop-flags.
//   acquire_impl(thr_clock, sync_clock);  // As usual, O(N).
//   sync_clock[tid].dirty = false;
//   sync_clock.dirty_count--;
// }
// The release operation looks as follows:
// void release(thr, tid, thr_clock, sync_clock) {
//   // thr->sync_cache is a simple fixed-size hash-based cache that holds
//   // several previous sync_clock's.
//   if (thr->sync_cache[sync_clock] >= thr->last_acquire_epoch) {
//     // The thread did no acquire operations since last release on this clock.
//     // So update only the thread's slot (other slots can't possibly change).
//     sync_clock[tid].clock = thr->epoch;
//     if (sync_clock.dirty_count == sync_clock.cnt
//         || (sync_clock.dirty_count == sync_clock.cnt - 1
//           && sync_clock[tid].dirty == false))
//       // All dirty flags are set, bail out.
//       return;
//     set all dirty bits, but preserve the thread's bit.  // O(N)
//     update sync_clock.dirty_count;
//     return;
//   }
//   release_impl(thr_clock, sync_clock);  // As usual, O(N).
//   set all dirty bits, but preserve the thread's bit.
//   // The previous step is combined with release_impl(), so that
//   // we scan the arrays only once.
//   update sync_clock.dirty_count;
// }

namespace __tsan {

ThreadClock::ThreadClock() {
  nclk_ = 0;
  for (uptr i = 0; i < (uptr)kMaxTidInClock; i++)
    clk_[i] = 0;
}

void ThreadClock::acquire(const SyncClock *src) {
  DCHECK(nclk_ <= kMaxTid);
  DCHECK(src->clk_.Size() <= kMaxTid);

  const uptr nclk = src->clk_.Size();
  if (nclk == 0)
    return;
  nclk_ = max(nclk_, nclk);
  for (uptr i = 0; i < nclk; i++) {
    if (clk_[i] < src->clk_[i])
      clk_[i] = src->clk_[i];
  }
}

void ThreadClock::release(SyncClock *dst) const {
  DCHECK(nclk_ <= kMaxTid);
  DCHECK(dst->clk_.Size() <= kMaxTid);

  if (dst->clk_.Size() < nclk_)
    dst->clk_.Resize(nclk_);
  for (uptr i = 0; i < nclk_; i++) {
    if (dst->clk_[i] < clk_[i])
      dst->clk_[i] = clk_[i];
  }
}

void ThreadClock::ReleaseStore(SyncClock *dst) const {
  DCHECK(nclk_ <= kMaxTid);
  DCHECK(dst->clk_.Size() <= kMaxTid);

  if (dst->clk_.Size() < nclk_)
    dst->clk_.Resize(nclk_);
  for (uptr i = 0; i < nclk_; i++)
    dst->clk_[i] = clk_[i];
  for (uptr i = nclk_; i < dst->clk_.Size(); i++)
    dst->clk_[i] = 0;
}

void ThreadClock::acq_rel(SyncClock *dst) {
  acquire(dst);
  release(dst);
}

void ThreadClock::Disable(unsigned tid) {
  u64 c0 = clk_[tid];
  for (uptr i = 0; i < kMaxTidInClock; i++)
    clk_[i] = (u64)-1;
  clk_[tid] = c0;
}

SyncClock::SyncClock()
  : clk_(MBlockClock) {
}
}  // namespace __tsan
<commit_msg>[TSan] delete trailing spaces<commit_after>//===-- tsan_clock.cc -----------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
#include "tsan_clock.h"
#include "tsan_rtl.h"

// It's possible to optimize clock operations for some important cases
// so that they are O(1). The cases include singletons, once's, local mutexes.
// First, SyncClock must be re-implemented to allow indexing by tid.
// It must not necessarily be a full vector clock, though. For example it may
// be a multi-level table.
// Then, each slot in SyncClock must contain a dirty bit (it's united with
// the clock value, so no space increase). The acquire algorithm looks
// as follows:
// void acquire(thr, tid, thr_clock, sync_clock) {
//   if (!sync_clock[tid].dirty)
//     return;  // No new info to acquire.
//              // This handles constant reads of singleton pointers and
//              // stop-flags.
//   acquire_impl(thr_clock, sync_clock);  // As usual, O(N).
//   sync_clock[tid].dirty = false;
//   sync_clock.dirty_count--;
// }
// The release operation looks as follows:
// void release(thr, tid, thr_clock, sync_clock) {
//   // thr->sync_cache is a simple fixed-size hash-based cache that holds
//   // several previous sync_clock's.
//   if (thr->sync_cache[sync_clock] >= thr->last_acquire_epoch) {
//     // The thread did no acquire operations since last release on this clock.
//     // So update only the thread's slot (other slots can't possibly change).
//     sync_clock[tid].clock = thr->epoch;
//     if (sync_clock.dirty_count == sync_clock.cnt
//         || (sync_clock.dirty_count == sync_clock.cnt - 1
//           && sync_clock[tid].dirty == false))
//       // All dirty flags are set, bail out.
//       return;
//     set all dirty bits, but preserve the thread's bit.  // O(N)
//     update sync_clock.dirty_count;
//     return;
//   }
//   release_impl(thr_clock, sync_clock);  // As usual, O(N).
//   set all dirty bits, but preserve the thread's bit.
//   // The previous step is combined with release_impl(), so that
//   // we scan the arrays only once.
//   update sync_clock.dirty_count;
// }

namespace __tsan {

ThreadClock::ThreadClock() {
  nclk_ = 0;
  for (uptr i = 0; i < (uptr)kMaxTidInClock; i++)
    clk_[i] = 0;
}

void ThreadClock::acquire(const SyncClock *src) {
  DCHECK(nclk_ <= kMaxTid);
  DCHECK(src->clk_.Size() <= kMaxTid);

  const uptr nclk = src->clk_.Size();
  if (nclk == 0)
    return;
  nclk_ = max(nclk_, nclk);
  for (uptr i = 0; i < nclk; i++) {
    if (clk_[i] < src->clk_[i])
      clk_[i] = src->clk_[i];
  }
}

void ThreadClock::release(SyncClock *dst) const {
  DCHECK(nclk_ <= kMaxTid);
  DCHECK(dst->clk_.Size() <= kMaxTid);

  if (dst->clk_.Size() < nclk_)
    dst->clk_.Resize(nclk_);
  for (uptr i = 0; i < nclk_; i++) {
    if (dst->clk_[i] < clk_[i])
      dst->clk_[i] = clk_[i];
  }
}

void ThreadClock::ReleaseStore(SyncClock *dst) const {
  DCHECK(nclk_ <= kMaxTid);
  DCHECK(dst->clk_.Size() <= kMaxTid);

  if (dst->clk_.Size() < nclk_)
    dst->clk_.Resize(nclk_);
  for (uptr i = 0; i < nclk_; i++)
    dst->clk_[i] = clk_[i];
  for (uptr i = nclk_; i < dst->clk_.Size(); i++)
    dst->clk_[i] = 0;
}

void ThreadClock::acq_rel(SyncClock *dst) {
  acquire(dst);
  release(dst);
}

void ThreadClock::Disable(unsigned tid) {
  u64 c0 = clk_[tid];
  for (uptr i = 0; i < kMaxTidInClock; i++)
    clk_[i] = (u64)-1;
  clk_[tid] = c0;
}

SyncClock::SyncClock()
  : clk_(MBlockClock) {
}
}  // namespace __tsan
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

Copyright (c) 2014 n@zgul <naazgull@dfz.pt>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <zapata/mongodb/Client.h>

zpt::mongodb::ClientPtr::ClientPtr(zpt::mongodb::Client * _target) : std::shared_ptr<zpt::mongodb::Client>(_target) {
}

zpt::mongodb::ClientPtr::ClientPtr(zpt::json _options, std::string _conf_path) : std::shared_ptr<zpt::mongodb::Client>(new zpt::mongodb::Client(_options, _conf_path)) {
}

zpt::mongodb::ClientPtr::~ClientPtr() {
}

zpt::mongodb::Client::Client(zpt::json _options, std::string _conf_path) : __options( _options), __mongodb_conf(_options->getPath(_conf_path)), __conf_path(_conf_path), __conn((string) __mongodb_conf["bind"]), __broadcast(true), __addons(new zpt::Addons(_options)) {
	if (this->__mongodb_conf["user"]->ok()) {
		this->__conn->auth(BSON("mechanism" << "MONGODB-CR" << "user" << (string) this->__mongodb_conf["user"] << "pwd" << (string) this->__mongodb_conf["passwd"] << "db" << (string) this->__mongodb_conf["db"]));
	}
	this->__conn->setWriteConcern((mongo::WriteConcern) 2);
}

zpt::mongodb::Client::~Client() {
	this->__conn.done();
}

zpt::json zpt::mongodb::Client::options() {
	return this->__options;
}

std::string zpt::mongodb::Client::name() {
	return string("mongodb://") + ((string) this->__mongodb_conf["bind"]) + string(":") + ((string) this->__mongodb_conf["port"]) + string("/") + ((string) this->__mongodb_conf["db"]);
}

bool& zpt::mongodb::Client::broadcast() {
	return this->__broadcast;
}

zpt::ev::emitter zpt::mongodb::Client::addons() {
	return this->__addons;
}

std::string zpt::mongodb::Client::insert(std::string _collection, std::string _id_prefix, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	if (!_document["id"]->ok()) {
		uuid _uuid;
		_uuid.make(UUID_MAKE_V1);
		_document << "id" << _uuid.string();
	}
	if (!_document["_id"]->ok() && _id_prefix.length() != 0) {
		_document << "_id" << (_id_prefix + (_id_prefix.back() != '/' ? string("/") : string("")) + _document["id"]->str());
	}
	_document << "href" << _document["_id"];

	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->insert(_full_collection, _mongo_document.obj());
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast && this->addons().get() != nullptr) {
		this->addons()->trigger(zpt::ev::Post, string("INSERT ") + _collection, _document);
	}
	return _document["id"]->str();
}

int zpt::mongodb::Client::save(std::string _collection, zpt::json _pattern, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->update(_full_collection, _filter, _mongo_document.obj(), false, false);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("UPDATE ") + _collection, _pattern);
	}
	return _size;
}

int zpt::mongodb::Client::set(std::string _collection, zpt::json _pattern, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	_document = { "$set", _document };
	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->update(_full_collection, _filter, _mongo_document.obj(), false, true);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("UPDATE ") + _collection, _pattern);
	}
	return _size;
}

int zpt::mongodb::Client::unset(std::string _collection, zpt::json _pattern, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	_document = { "$unset", _document };
	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->update(_full_collection, _filter, _mongo_document.obj(), false, true);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("UPDATE ") + _collection, _pattern);
	}
	return _size;
}

int zpt::mongodb::Client::remove(std::string _collection, zpt::json _pattern) {
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	this->__conn->remove(_full_collection, _filter);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("DELETE ") + _collection, _pattern);
	}
	return _size;
}

zpt::json zpt::mongodb::Client::query(std::string _collection, zpt::json _pattern) {
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	zpt::JSONArr _elements;

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);

	mongo::Query _query(_query_b.done());
	cout << _query.obj.jsonString(mongo::JS) << endl << flush;
	unsigned long _size = this->__conn->count(_full_collection, _query.obj, (int) mongo::QueryOption_SlaveOk);
	mongo::BSONObj _order = _order_b.done();
	if (!_order.isEmpty()) {
		_query.sort(_order);
	}

	std::unique_ptr<mongo::DBClientCursor> _result = this->__conn->query(_full_collection, _query, _page_size, _page_start_index, nullptr, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	while(_result->more()) {
		mongo::BSONObj _record = _result->next();
		zpt::JSONObj _obj;
		zpt::mongodb::frommongo(_record, _obj);
		_elements << _obj;
	}

	if (_elements->size() == 0) {
		return zpt::undefined;
	}
	zpt::json _return = {
		"size", _size,
		"elements", _elements
	};
	if (_page_size != 0) {
		_return << "links" << zpt::json(
			{
				"next", (std::string("?page-size=") + std::to_string(_page_size) + std::string("&page-start-index=") + std::to_string(_page_start_index + _page_size)),
				"prev", (std::string("?page-size=") + std::to_string(_page_size) + std::string("&page-start-index=") + std::to_string(_page_size < _page_start_index ? _page_start_index - _page_size : 0))
			}
		);
	}
	return _return;
}
<commit_msg>added support for object and array types in query<commit_after>/*
The MIT License (MIT)

Copyright (c) 2014 n@zgul <naazgull@dfz.pt>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <zapata/mongodb/Client.h>

zpt::mongodb::ClientPtr::ClientPtr(zpt::mongodb::Client * _target) : std::shared_ptr<zpt::mongodb::Client>(_target) {
}

zpt::mongodb::ClientPtr::ClientPtr(zpt::json _options, std::string _conf_path) : std::shared_ptr<zpt::mongodb::Client>(new zpt::mongodb::Client(_options, _conf_path)) {
}

zpt::mongodb::ClientPtr::~ClientPtr() {
}

zpt::mongodb::Client::Client(zpt::json _options, std::string _conf_path) : __options( _options), __mongodb_conf(_options->getPath(_conf_path)), __conf_path(_conf_path), __conn((string) __mongodb_conf["bind"]), __broadcast(true), __addons(new zpt::Addons(_options)) {
	if (this->__mongodb_conf["user"]->ok()) {
		this->__conn->auth(BSON("mechanism" << "MONGODB-CR" << "user" << (string) this->__mongodb_conf["user"] << "pwd" << (string) this->__mongodb_conf["passwd"] << "db" << (string) this->__mongodb_conf["db"]));
	}
	this->__conn->setWriteConcern((mongo::WriteConcern) 2);
}

zpt::mongodb::Client::~Client() {
	this->__conn.done();
}

zpt::json zpt::mongodb::Client::options() {
	return this->__options;
}

std::string zpt::mongodb::Client::name() {
	return string("mongodb://") + ((string) this->__mongodb_conf["bind"]) + string(":") + ((string) this->__mongodb_conf["port"]) + string("/") + ((string) this->__mongodb_conf["db"]);
}

bool& zpt::mongodb::Client::broadcast() {
	return this->__broadcast;
}

zpt::ev::emitter zpt::mongodb::Client::addons() {
	return this->__addons;
}

std::string zpt::mongodb::Client::insert(std::string _collection, std::string _id_prefix, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	if (!_document["id"]->ok()) {
		uuid _uuid;
		_uuid.make(UUID_MAKE_V1);
		_document << "id" << _uuid.string();
	}
	if (!_document["_id"]->ok() && _id_prefix.length() != 0) {
		_document << "_id" << (_id_prefix + (_id_prefix.back() != '/' ? string("/") : string("")) + _document["id"]->str());
	}
	_document << "href" << _document["_id"];

	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->insert(_full_collection, _mongo_document.obj());
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast && this->addons().get() != nullptr) {
		this->addons()->trigger(zpt::ev::Post, string("INSERT ") + _collection, _document);
	}
	return _document["id"]->str();
}

int zpt::mongodb::Client::save(std::string _collection, zpt::json _pattern, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->update(_full_collection, _filter, _mongo_document.obj(), false, false);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("UPDATE ") + _collection, _pattern);
	}
	return _size;
}

int zpt::mongodb::Client::set(std::string _collection, zpt::json _pattern, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	_document = { "$set", _document };
	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->update(_full_collection, _filter, _mongo_document.obj(), false, true);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("UPDATE ") + _collection, _pattern);
	}
	return _size;
}

int zpt::mongodb::Client::unset(std::string _collection, zpt::json _pattern, zpt::json _document) {
	assertz(_document->ok() && _document->type() == zpt::JSObject, "'_document' must be of type JSObject", 412, 0);
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	_document = { "$unset", _document };
	mongo::BSONObjBuilder _mongo_document;
	zpt::mongodb::tomongo(_document, _mongo_document);
	this->__conn->update(_full_collection, _filter, _mongo_document.obj(), false, true);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("UPDATE ") + _collection, _pattern);
	}
	return _size;
}

int zpt::mongodb::Client::remove(std::string _collection, zpt::json _pattern) {
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);
	mongo::Query _filter(_query_b.done());

	unsigned long _size = this->__conn->count(_full_collection, _filter.obj, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	this->__conn->remove(_full_collection, _filter);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	if (this->__broadcast) {
		this->addons()->trigger(zpt::ev::Post, string("DELETE ") + _collection, _pattern);
	}
	return _size;
}

zpt::json zpt::mongodb::Client::query(std::string _collection, zpt::json _pattern) {
	if (!_pattern->ok()) {
		_pattern = zpt::mkobj();
	}

	zpt::JSONArr _elements;

	std::string _full_collection(_collection);
	_full_collection.insert(0, ".");
	_full_collection.insert(0, (string) this->__mongodb_conf["db"]);

	size_t _page_size = 0;
	size_t _page_start_index = 0;
	mongo::BSONObjBuilder _query_b;
	mongo::BSONObjBuilder _order_b;
	zpt::mongodb::get_query(_pattern, _query_b, _order_b, _page_size, _page_start_index);

	mongo::Query _query(_query_b.done());
	unsigned long _size = this->__conn->count(_full_collection, _query.obj, (int) mongo::QueryOption_SlaveOk);
	mongo::BSONObj _order = _order_b.done();
	if (!_order.isEmpty()) {
		_query.sort(_order);
	}

	std::unique_ptr<mongo::DBClientCursor> _result = this->__conn->query(_full_collection, _query, _page_size, _page_start_index, nullptr, (int) mongo::QueryOption_SlaveOk);
	assertz(this->__conn->getLastError().length() == 0, string("mongodb operation returned an error: ") + this->__conn->getLastError(), 500, 0);

	while(_result->more()) {
		mongo::BSONObj _record = _result->next();
		zpt::JSONObj _obj;
		zpt::mongodb::frommongo(_record, _obj);
		_elements << _obj;
	}

	if (_elements->size() == 0) {
		return zpt::undefined;
	}
	zpt::json _return = {
		"size", _size,
		"elements", _elements
	};
	if (_page_size != 0) {
		_return << "links" << zpt::json(
			{
				"next", (std::string("?page-size=") + std::to_string(_page_size) + std::string("&page-start-index=") + std::to_string(_page_start_index + _page_size)),
				"prev", (std::string("?page-size=") + std::to_string(_page_size) + std::string("&page-start-index=") + std::to_string(_page_size < _page_start_index ? _page_start_index - _page_size : 0))
			}
		);
	}
	return _return;
}
<|endoftext|>


<commit_before>/*
 * gnd_linalg.hpp
 *
 *  Created on: 2012/01/22
 *      Author: tyamada
 */

#include "gnd-vector-base.hpp"
#include "gnd-matrix-base.hpp"

/**
 * @ifnot GNDLinAlg
 * @defgroup GNDLinAlg linear-algebras
 * @endif GNDLinAlg
 */
namespace gnd {
	/**
	 * @ingroup GNDLinAlg
	 * @namespace linalg
	 * supply linear-algebras
	 */
	namespace linalg {
		/**
		 * @ingroup GNDLinAlg
		 * @brief cholesky matrix decomposition
		 */
		template < typename MTRX  >
		inline int cholesky_decomposition(MTRX *v, const size_t n)
		{
			gnd_assert(_gnd_matrix_row_(v) < n, -1, "invalid matrix property");
			gnd_assert(_gnd_matrix_column_(v) < n, -1,  "invalid matrix property");
			gnd_error(n == 0, 0, "this argument have no effect");

			{ // ---> operation
				size_t i, j;
				double l00;

				gnd_error(_gnd_matrix_ref_(v, 0, 0) <= 0, -1, "general failure.");
				l00 = ::sqrt(_gnd_matrix_ref_(v, 0, 0));
				_gnd_matrix_ref_(v, 0, 0) = l00;

				if(n > 1){
					double l10 = _gnd_matrix_ref_(v, 1, 0) / l00;
					double diag = _gnd_matrix_ref_(v, 1, 1) - l10 * l10;
					double l11;

					gnd_error(diag < 0, -1, "general failure.");
					l11 = ::sqrt(diag);

					_gnd_matrix_ref_(v, 1, 0) = l10;
					_gnd_matrix_ref_(v, 1, 1) = l11;
				}

				// cholesky decomposition
				for(j = 2; j < n; j++){
					for(i = 0; i < j; i++){
						double sum = 0;
						double aji = _gnd_matrix_ref_(v, j, i);
						double aii = _gnd_matrix_ref_(v, i, i);
						double lji;

						if(i != 0){
							gnd::vector::flex di;
							gnd::vector::flex dj;

							gnd::vector::init(&di);
							gnd::vector::init(&dj);

							gnd::matrix::assign_to_as_vector(&di, v, i, 0, i);
							gnd::matrix::assign_to_as_vector(&dj, v, j, 0, i);

							gnd::matrix::inner_prod(&di, &dj, &sum);
						}
						gnd_error(aii == 0, -1, "general failure.");
						lji = (aji - sum) / aii;

						_gnd_matrix_ref_(v, j, i) = lji;
					} // for(i)

					{ // ---> diagonal
						gnd::matrix::flex dj;
						double sum;
						double diag;
						double ljj;

						gnd::vector::init(&dj);
						gnd::matrix::assign_to_as_vector(&dj, v, j, 0, j);

						gnd::vector::sqnorm(&dj, &sum);

						diag = _gnd_matrix_ref_(v, j, j) - sum;
						gnd_error(diag < 0, -1, "general failure.");
						ljj = ::sqrt(diag);
						_gnd_matrix_ref_(v, j, j) = ljj;
					} // <--- diagonal

				}// for(j)

				for(i = 0; i < n; i++) {
					for(j = i + 1; j < n; j++) {
						_gnd_matrix_ref_(v, i, j) = 0;
					}
				}

			} // <--- operation
			return 0;
		}

	} // <--- namespace linalg
} // <--- namespace gnd

<commit_msg>update bug fix<commit_after>/*
 * gnd_linalg.hpp
 *
 *  Created on: 2012/01/22
 *      Author: tyamada
 */

#include "gnd-vector-base.hpp"
#include "gnd-matrix-base.hpp"

#ifndef GND_LINALG
#define GND_LINALG
/**
 * @ifnot GNDLinAlg
 * @defgroup GNDLinAlg linear-algebras
 * @endif GNDLinAlg
 */
namespace gnd {
	/**
	 * @ingroup GNDLinAlg
	 * @namespace linalg
	 * supply linear-algebras
	 */
	namespace linalg {
		/**
		 * @ingroup GNDLinAlg
		 * @brief cholesky matrix decomposition
		 */
		template < typename MTRX  >
		inline int cholesky_decomposition(MTRX *v, const size_t n)
		{
			gnd_assert(_gnd_matrix_row_(v) < n, -1, "invalid matrix property");
			gnd_assert(_gnd_matrix_column_(v) < n, -1,  "invalid matrix property");
			gnd_error(n == 0, 0, "this argument have no effect");

			{ // ---> operation
				size_t i, j;
				double l00;

				gnd_error(_gnd_matrix_ref_(v, 0, 0) <= 0, -1, "general failure.");
				l00 = ::sqrt(_gnd_matrix_ref_(v, 0, 0));
				_gnd_matrix_ref_(v, 0, 0) = l00;

				if(n > 1){
					double l10 = _gnd_matrix_ref_(v, 1, 0) / l00;
					double diag = _gnd_matrix_ref_(v, 1, 1) - l10 * l10;
					double l11;

					gnd_error(diag < 0, -1, "general failure.");
					l11 = ::sqrt(diag);

					_gnd_matrix_ref_(v, 1, 0) = l10;
					_gnd_matrix_ref_(v, 1, 1) = l11;
				}

				// cholesky decomposition
				for(j = 2; j < n; j++){
					for(i = 0; i < j; i++){
						double sum = 0;
						double aji = _gnd_matrix_ref_(v, j, i);
						double aii = _gnd_matrix_ref_(v, i, i);
						double lji;

						if(i != 0){
							gnd::vector::flex di;
							gnd::vector::flex dj;

							gnd::vector::init(&di);
							gnd::vector::init(&dj);

							gnd::matrix::assign_to_as_vector(&di, v, i, 0, i);
							gnd::matrix::assign_to_as_vector(&dj, v, j, 0, i);

							gnd::matrix::inner_prod(&di, &dj, &sum);
						}
						gnd_error(aii == 0, -1, "general failure.");
						lji = (aji - sum) / aii;

						_gnd_matrix_ref_(v, j, i) = lji;
					} // for(i)

					{ // ---> diagonal
						gnd::matrix::flex dj;
						double sum;
						double diag;
						double ljj;

						gnd::vector::init(&dj);
						gnd::matrix::assign_to_as_vector(&dj, v, j, 0, j);

						gnd::vector::sqnorm(&dj, &sum);

						diag = _gnd_matrix_ref_(v, j, j) - sum;
						gnd_error(diag < 0, -1, "general failure.");
						ljj = ::sqrt(diag);
						_gnd_matrix_ref_(v, j, j) = ljj;
					} // <--- diagonal

				}// for(j)

				for(i = 0; i < n; i++) {
					for(j = i + 1; j < n; j++) {
						_gnd_matrix_ref_(v, i, j) = 0;
					}
				}

			} // <--- operation
			return 0;
		}

	} // <--- namespace linalg
} // <--- namespace gnd

#endif
<|endoftext|>


<commit_before>/*
 * ComponentManager.cpp
 *
 *  Created on: Dec 17, 2013
 *      Author: Dawid Drozd
 */

#include "component/ComponentManager.h"
#include "component/Component.h"

#define UNUSED_TAG -1

namespace KoalaComponent
{

ComponentManager::ComponentManager ( CCNode* pWorkingNode ,
									 Notifier& pNotifierNode ) :
	m_pWorkingNode ( pWorkingNode )
	, m_nodeNotifier ( pNotifierNode )
	, m_pOwner ( nullptr )
{
	m_componentTags.reserve ( 16 );
	m_components.reserve ( 16 );
}

ComponentManager::~ComponentManager()
{
	removeAllComponents();
	m_pWorkingNode = nullptr;
}

void ComponentManager::addComponent ( Component* const pComponent )
{
	addComponent ( pComponent, UNUSED_TAG );
}

void ComponentManager::addComponent ( Component* const pComponent ,
									  const int tag )
{
#ifdef DEBUG

	for ( auto && tagAdded : m_componentTags )
	{
		if ( tagAdded == tag && tag != UNUSED_TAG )
		{
			CCAssert ( false, "You can't add component with the same tag again." );
		}
	}

	for ( auto pElement : m_components )
	{
		if ( pElement == pComponent )
		{
			CCAssert ( false, "This component was already added" );
		}
	}

#endif
	m_componentTags.push_back ( tag );
	m_components.push_back ( pComponent );
	pComponent->retain();

	pComponent->setOwner ( this );
}

void ComponentManager::removeAllComponents()
{
	m_componentTags.clear();

	Component* pComponent = nullptr;

	while ( m_components.empty() == false )
	{
		pComponent = m_components.back();

		m_nodeNotifier.notify ( getNotificationOnBeforeRemoveFromComponentNode(), pComponent );
		//We must remove all potential listeners because when we are using notifier from
		//ComponentNode so we don't have to unregister in their destructors
		m_nodeNotifier.removeAllForObject ( pComponent );

		m_components.pop_back();

		pComponent->removeOwner();
		pComponent->release();
	}
}

void ComponentManager::removeComponent ( const int tag )
{
	CCAssert ( tag != UNUSED_TAG,
			   "can't delete component with this kind of tag" );
	int i = 0;

	for ( auto && componentTag : m_componentTags )
	{
		if ( componentTag == tag )
		{
			removeComponentAtPosition ( i );
			return;
		}

		++i;
	}
}

void ComponentManager::removeComponent ( Component* const pComponent )
{
	CCAssert ( pComponent != nullptr,
			   "pComponent can't be null" );
	int i = 0;

	for ( Component* const pElement : m_components )
	{
		if ( pElement == pComponent )
		{
			removeComponentAtPosition ( i );
			return;
		}

		++i;
	}
}

Component* ComponentManager::getComponent ( int tag )
{
	CCAssert ( tag != UNUSED_TAG,
			   "can't find component with this kind of tag" );
	int i = 0;

	for ( int componentTag : m_componentTags )
	{
		if ( componentTag == tag )
		{
			return m_components[i];
		}

		++i;
	}

	return nullptr;
}

void ComponentManager::removeComponentAtPosition ( const int index )
{
	assert ( index < ( int ) m_components.size() );
	Component* pComponent = m_components[index];

	m_nodeNotifier.notify ( getNotificationOnBeforeRemoveFromComponentNode(), pComponent );
	//We must remove all potential listeners because when we are using notifier from
	//ComponentNode so we don't have to unregister in destructors
	m_nodeNotifier.removeAllForObject ( pComponent );

	std::swap ( m_components[index], m_components.back() );
	std::swap ( m_componentTags[index], m_componentTags.back() );
	m_components.pop_back();
	m_componentTags.pop_back();

	m_nodeNotifier.removeAllForObject ( pComponent );

	pComponent->release();
}

void ComponentManager::setOwner ( Component* pComponentOwner )
{
	assert ( m_pOwner == nullptr );
	m_pOwner = pComponentOwner;
}

} /* namespace KoalaComponent */

<commit_msg>Fix for special case<commit_after>/*
 * ComponentManager.cpp
 *
 *  Created on: Dec 17, 2013
 *      Author: Dawid Drozd
 */

#include "component/ComponentManager.h"
#include "component/Component.h"

#define UNUSED_TAG -1

namespace KoalaComponent
{

ComponentManager::ComponentManager ( CCNode* pWorkingNode ,
									 Notifier& pNotifierNode ) :
	m_pWorkingNode ( pWorkingNode )
	, m_nodeNotifier ( pNotifierNode )
	, m_pOwner ( nullptr )
{
	m_componentTags.reserve ( 16 );
	m_components.reserve ( 16 );
}

ComponentManager::~ComponentManager()
{
	removeAllComponents();
	m_pWorkingNode = nullptr;
}

void ComponentManager::addComponent ( Component* const pComponent )
{
	addComponent ( pComponent, UNUSED_TAG );
}

void ComponentManager::addComponent ( Component* const pComponent ,
									  const int tag )
{
#ifdef DEBUG

	for ( auto && tagAdded : m_componentTags )
	{
		if ( tagAdded == tag && tag != UNUSED_TAG )
		{
			CCAssert ( false, "You can't add component with the same tag again." );
		}
	}

	for ( auto pElement : m_components )
	{
		if ( pElement == pComponent )
		{
			CCAssert ( false, "This component was already added" );
		}
	}

#endif
	m_componentTags.push_back ( tag );
	m_components.push_back ( pComponent );
	pComponent->retain();
	//Extra retain for case when in init we remove self
	pComponent->retain();
	{
		pComponent->setOwner ( this );
	}
	pComponent->release();//Remove extra retain
}

void ComponentManager::removeAllComponents()
{
	m_componentTags.clear();

	Component* pComponent = nullptr;

	while ( m_components.empty() == false )
	{
		pComponent = m_components.back();

		m_nodeNotifier.notify ( getNotificationOnBeforeRemoveFromComponentNode(), pComponent );
		//We must remove all potential listeners because when we are using notifier from
		//ComponentNode so we don't have to unregister in their destructors
		m_nodeNotifier.removeAllForObject ( pComponent );

		m_components.pop_back();

		pComponent->removeOwner();
		pComponent->release();
	}
}

void ComponentManager::removeComponent ( const int tag )
{
	CCAssert ( tag != UNUSED_TAG,
			   "can't delete component with this kind of tag" );
	int i = 0;

	for ( auto && componentTag : m_componentTags )
	{
		if ( componentTag == tag )
		{
			removeComponentAtPosition ( i );
			return;
		}

		++i;
	}
}

void ComponentManager::removeComponent ( Component* const pComponent )
{
	CCAssert ( pComponent != nullptr,
			   "pComponent can't be null" );
	int i = 0;

	for ( Component* const pElement : m_components )
	{
		if ( pElement == pComponent )
		{
			removeComponentAtPosition ( i );
			return;
		}

		++i;
	}
}

Component* ComponentManager::getComponent ( int tag )
{
	CCAssert ( tag != UNUSED_TAG,
			   "can't find component with this kind of tag" );
	int i = 0;

	for ( int componentTag : m_componentTags )
	{
		if ( componentTag == tag )
		{
			return m_components[i];
		}

		++i;
	}

	return nullptr;
}

void ComponentManager::removeComponentAtPosition ( const int index )
{
	assert ( index < ( int ) m_components.size() );
	Component* pComponent = m_components[index];

	m_nodeNotifier.notify ( getNotificationOnBeforeRemoveFromComponentNode(), pComponent );
	//We must remove all potential listeners because when we are using notifier from
	//ComponentNode so we don't have to unregister in destructors
	m_nodeNotifier.removeAllForObject ( pComponent );

	std::swap ( m_components[index], m_components.back() );
	std::swap ( m_componentTags[index], m_componentTags.back() );
	m_components.pop_back();
	m_componentTags.pop_back();

	m_nodeNotifier.removeAllForObject ( pComponent );

	pComponent->release();
}

void ComponentManager::setOwner ( Component* pComponentOwner )
{
	assert ( m_pOwner == nullptr );
	m_pOwner = pComponentOwner;
}

} /* namespace KoalaComponent */

<|endoftext|>