//===--- EncodedSequence.h - A byte-encoded sequence ------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines a data structure which stores a heterogeneous // sequence of byte-encoded values. // // The data structure is optimized to minimize its required storage // under the assumption that the sequence is usually very short. // //===----------------------------------------------------------------------===// #ifndef SWIFT_BASIC_ENCODEDSEQUENCE_H #define SWIFT_BASIC_ENCODEDSEQUENCE_H #include "swift/Basic/Compiler.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/PrefixMap.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/TargetParser/Host.h" #include "llvm/Support/TrailingObjects.h" #include namespace swift { /// A base class which handles most of the memory management. class EncodedSequenceBase { public: /// A chunk of individual path data. A pointer to this type will be /// used to access the Data field, so either this type needs to have /// equivalent aliasing power to 'char' or that constraint must be /// expressible in some other way. using Chunk = unsigned char; private: enum { IsInline = 0x1, InitialDataValue = IsInline, IndexBitsPerChunk = CHAR_BIT * sizeof(Chunk) - 1, IndexContinuesMask = 1 << IndexBitsPerChunk, IndexChunkMask = IndexContinuesMask - 1, }; /// Either a Chunk* or an array of Chunks. uintptr_t Data; /// Return whether we're using out-of-line storage. bool hasOutOfLineStorage() const { return !(Data & IsInline); } unsigned getInlineSize() const { assert(!hasOutOfLineStorage()); return (Data & 0xFF) >> 1; } void setInlineSize(unsigned size) { assert(!hasOutOfLineStorage()); Data = (Data & ~uintptr_t(0xFF)) | (size << 1) | IsInline; } /// A structure representing out-of-line chunk storage. struct OutOfLineStorage final : private llvm::TrailingObjects { friend TrailingObjects; uint16_t Size; uint16_t Capacity; OutOfLineStorage(size_t capacity) : Size(0), Capacity(capacity) {} public: OutOfLineStorage *clone(size_t newCapacity) const { assert(newCapacity >= Size); size_t newAllocSize = totalSizeToAlloc(newCapacity); auto newData = new (operator new(newAllocSize)) OutOfLineStorage(newCapacity); newData->setSize(Size); std::uninitialized_copy(chunks().begin(), chunks().end(), newData->chunkStorage().begin()); return newData; } static OutOfLineStorage *alloc(size_t newSizeInBytes) { auto capacity = (newSizeInBytes - sizeof(OutOfLineStorage)) / sizeof(Chunk); return new (operator new(newSizeInBytes)) OutOfLineStorage(capacity); } size_t getCapacity() const { return Capacity; } void setSize(size_t newSize) { assert(newSize <= Capacity); Size = newSize; } MutableArrayRef chunkStorage() { return {getTrailingObjects(), Capacity}; } ArrayRef chunkStorage() const { return {getTrailingObjects(), Capacity}; } ArrayRef chunks() const { return {getTrailingObjects(), Size}; } }; OutOfLineStorage *getOutOfLineStorage() { assert(hasOutOfLineStorage()); return reinterpret_cast(Data); } const OutOfLineStorage *getOutOfLineStorage() const { return const_cast(this)->getOutOfLineStorage(); } /// Return the array of possibly-initialized storage chunks. /// The bound is the capacity of the storage. MutableArrayRef chunkStorage() { if (hasOutOfLineStorage()) return getOutOfLineStorage()->chunkStorage(); // The legality of accesses from this cast depends on Chunk having // all-powerful aliasing rights. Chunk *array = reinterpret_cast(&Data); if (llvm::sys::IsLittleEndianHost) array++; return MutableArrayRef(array, sizeof(Data) / sizeof(Chunk) - 1); } ArrayRef chunkStorage() const { return const_cast(this)->chunkStorage(); } void cloneOutOfLineStorage() { auto oldStorage = getOutOfLineStorage(); Data = reinterpret_cast( oldStorage->clone(oldStorage->getCapacity())); } void destroyOutOfLineStorage() { auto oldStorage = getOutOfLineStorage(); delete oldStorage; } public: EncodedSequenceBase() : Data(InitialDataValue) {} EncodedSequenceBase(const EncodedSequenceBase &other) : Data(other.Data) { if (hasOutOfLineStorage()) cloneOutOfLineStorage(); } EncodedSequenceBase(EncodedSequenceBase &&other) : Data(other.Data) { other.Data = InitialDataValue; } EncodedSequenceBase &operator=(const EncodedSequenceBase &other) { if (hasOutOfLineStorage()) { // Try to copy into the existing storage. auto otherChunks = other.chunks(); auto selfStorage = getOutOfLineStorage(); if (otherChunks.size() <= selfStorage->getCapacity()) { memcpy(selfStorage->chunkStorage().data(), otherChunks.data(), sizeof(Chunk) * otherChunks.size()); selfStorage->setSize(otherChunks.size()); return *this; } // Otherwise, destroy the existing storage. destroyOutOfLineStorage(); } Data = other.Data; if (hasOutOfLineStorage()) cloneOutOfLineStorage(); return *this; } EncodedSequenceBase &operator=(EncodedSequenceBase &&other) { if (hasOutOfLineStorage()) destroyOutOfLineStorage(); Data = other.Data; other.Data = InitialDataValue; return *this; } ~EncodedSequenceBase() { if (hasOutOfLineStorage()) destroyOutOfLineStorage(); } protected: /// Return the array of initialized storage chunks. /// The bound is the actual length of the storage. ArrayRef chunks() const { if (hasOutOfLineStorage()) { return getOutOfLineStorage()->chunks(); } else { return chunkStorage().slice(0, getInlineSize()); } } /// Claim N more storage chunks, reallocating the storage if necessary. MutableArrayRef claimStorage(size_t extra) { MutableArrayRef oldStorage = chunkStorage(); auto oldSize = chunks().size(); auto newSize = oldSize + extra; // If the existing storage has enough space, we're fine. if (newSize <= oldStorage.size()) { if (hasOutOfLineStorage()) { getOutOfLineStorage()->setSize(newSize); } else { setInlineSize(newSize); } return oldStorage.slice(oldSize); } // Otherwise, allocate out-of-line storage. size_t newAllocSize = 32; while (newSize * sizeof(Chunk) + sizeof(OutOfLineStorage) > newAllocSize) newAllocSize *= 2; auto newStorage = OutOfLineStorage::alloc(newAllocSize); // Copy from the old storage. newStorage->setSize(newSize); memcpy(newStorage->chunkStorage().data(), oldStorage.data(), oldSize * sizeof(Chunk)); // Destroy the old storage. if (hasOutOfLineStorage()) destroyOutOfLineStorage(); // Install the new storage and return. Data = reinterpret_cast(newStorage); return newStorage->chunkStorage().slice(oldSize); } public: /// A convenience routine for decoding an unsigned value out of a /// sequence of chunks. static unsigned decodeIndex(const Chunk *&ptr) { unsigned value = 0; unsigned shift = 0; while (true) { unsigned chunk = *ptr++; if (chunk & IndexContinuesMask) { value |= (chunk ^ IndexContinuesMask) << shift; } else { value |= chunk << shift; break; } shift += IndexBitsPerChunk; } return value; } /// A convenience routine for encoding an unsigned value as a /// sequence of chunks. static void encodeIndex(unsigned value, Chunk *&ptr) { do { Chunk chunk = (value & IndexChunkMask); auto nextValue = (value >> IndexBitsPerChunk); if (nextValue) chunk |= IndexContinuesMask; *ptr++ = chunk; value = nextValue; } while (value); } /// Returns the encoding size required by the above convenience /// routine for the given unsigned value. static unsigned getEncodedIndexSize(unsigned value) { unsigned count = 0; do { count++; value >>= IndexBitsPerChunk; } while (value); return count; } }; /// An encoded sequence. /// /// The Element type is assumed to have the following members: /// /// static Element decode(const EncodedSequenceBase::Chunk *&ptr); /// void encode(EncodedSequenceBase::Chunk *&ptr) const; /// unsigned getEncodedSize() const; /// /// encode and decode should advance the pointer by exactly the number /// of chunks returned by getEncodedSize. template class EncodedSequence : public EncodedSequenceBase { public: /// An iterator over an encoded sequence. class iterator { const Chunk *Ptr; public: iterator() = default; explicit iterator(const Chunk *ptr) : Ptr(ptr) {} Element operator*() const { // Decode without changing Ptr. auto ptr = Ptr; return Element::decode(ptr); } iterator &operator++() { Element::decode(Ptr); return *this; } iterator operator++(int) { auto copy = *this; operator++(); return copy; } bool operator==(iterator other) const { return Ptr == other.Ptr; } bool operator!=(iterator other) const { return Ptr != other.Ptr; } const Chunk *getRawChunkPtr() const { return Ptr; }; }; iterator begin() const { return iterator(chunks().begin()); } iterator end() const { return iterator(chunks().end()); } bool empty() const { return begin() == end(); } /// Add a new element to the sequence. void push_back(Element elt) { // Figure out how much storage we need. unsigned encodedSize = elt.getEncodedSize(); // Claim that much storage, growing if necessary. MutableArrayRef storage = claimStorage(encodedSize); // Initialize the storage. Chunk *ptr = storage.data(); elt.encode(ptr); } /// A mapping from encoded sequences to some sort of value. /// /// This class is just a trivial type-adjusting wrapper around PrefixMap; /// see the documentation there for information about how to use this /// data structure. template class Map { // Hack: MSVC isn't able to resolve the InlineKeyCapacity part of the // template of PrefixMap, so we have to split it up and pass it manually. #if SWIFT_COMPILER_IS_MSVC && _MSC_VER < 1910 static const size_t Size = (sizeof(void*) - 1) / sizeof(Chunk); static const size_t ActualSize = max(Size, 1); using MapBase = PrefixMap; #else using MapBase = PrefixMap; #endif MapBase TheMap; public: using SequenceIterator = typename EncodedSequence::iterator; using KeyType = typename MapBase::KeyType; using Handle = typename MapBase::Handle; std::pair findPrefix(SequenceIterator begin, SequenceIterator end) const { auto result = TheMap.findPrefix(getKey(begin, end)); return { result.first, SequenceIterator(result.second) }; } template std::pair insert(SequenceIterator begin, SequenceIterator end, T &&value) { return TheMap.insert(getKey(begin, end), std::forward(value)); } template Handle insertNew(SequenceIterator begin, SequenceIterator end, T &&value) { return TheMap.insertNew(getKey(begin, end), std::forward(value)); } private: static KeyType getKey(SequenceIterator begin, SequenceIterator end) { return KeyType(begin.getRawChunkPtr(), end.getRawChunkPtr()); } }; }; } // end namespace swift #endif // SWIFT_BASIC_ENCODEDSEQUENCE_H