| //===--- 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/Support/Host.h" |
| #include "llvm/Support/TrailingObjects.h" |
| #include <climits> |
| |
| 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<OutOfLineStorage, Chunk> { |
| 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<Chunk>(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<Chunk> chunkStorage() { |
| return {getTrailingObjects<Chunk>(), Capacity}; |
| } |
| ArrayRef<Chunk> chunkStorage() const { |
| return {getTrailingObjects<Chunk>(), Capacity}; |
| } |
| ArrayRef<Chunk> chunks() const { |
| return {getTrailingObjects<Chunk>(), Size}; |
| } |
| }; |
| OutOfLineStorage *getOutOfLineStorage() { |
| assert(hasOutOfLineStorage()); |
| return reinterpret_cast<OutOfLineStorage*>(Data); |
| } |
| const OutOfLineStorage *getOutOfLineStorage() const { |
| return const_cast<EncodedSequenceBase*>(this)->getOutOfLineStorage(); |
| } |
| |
| /// Return the array of possibly-initialized storage chunks. |
| /// The bound is the capacity of the storage. |
| MutableArrayRef<Chunk> 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<Chunk*>(&Data); |
| if (llvm::sys::IsLittleEndianHost) array++; |
| return MutableArrayRef<Chunk>(array, sizeof(Data) / sizeof(Chunk) - 1); |
| } |
| ArrayRef<Chunk> chunkStorage() const { |
| return const_cast<EncodedSequenceBase*>(this)->chunkStorage(); |
| } |
| |
| void cloneOutOfLineStorage() { |
| auto oldStorage = getOutOfLineStorage(); |
| Data = reinterpret_cast<uintptr_t>( |
| 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<Chunk> chunks() const { |
| if (hasOutOfLineStorage()) { |
| return getOutOfLineStorage()->chunks(); |
| } else { |
| return chunkStorage().slice(0, getInlineSize()); |
| } |
| } |
| |
| /// Claim N more storage chunks, reallocating the storage if necessary. |
| MutableArrayRef<Chunk> claimStorage(size_t extra) { |
| MutableArrayRef<Chunk> 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<uintptr_t>(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 Element> |
| 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<Chunk> 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 ValueType> 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_t>(Size, 1); |
| |
| using MapBase = PrefixMap<Chunk, ValueType, ActualSize>; |
| #else |
| using MapBase = PrefixMap<Chunk, ValueType>; |
| #endif |
| MapBase TheMap; |
| |
| public: |
| using SequenceIterator = typename EncodedSequence::iterator; |
| using KeyType = typename MapBase::KeyType; |
| using Handle = typename MapBase::Handle; |
| |
| std::pair<Handle, SequenceIterator> |
| findPrefix(SequenceIterator begin, SequenceIterator end) const { |
| auto result = TheMap.findPrefix(getKey(begin, end)); |
| return { result.first, SequenceIterator(result.second) }; |
| } |
| |
| template <class T> |
| std::pair<Handle, bool> |
| insert(SequenceIterator begin, SequenceIterator end, T &&value) { |
| return TheMap.insert(getKey(begin, end), std::forward<T>(value)); |
| } |
| |
| template <class T> |
| Handle insertNew(SequenceIterator begin, SequenceIterator end, T &&value) { |
| return TheMap.insertNew(getKey(begin, end), std::forward<T>(value)); |
| } |
| |
| private: |
| static KeyType getKey(SequenceIterator begin, SequenceIterator end) { |
| return KeyType(begin.getRawChunkPtr(), end.getRawChunkPtr()); |
| } |
| }; |
| }; |
| |
| } // end namespace swift |
| |
| #endif // SWIFT_BASIC_ENCODEDSEQUENCE_H |