summaryrefslogtreecommitdiffstats
path: root/source/core/slang-string-slice-pool.h
blob: 87ca18e163b48f623ba5053e840bcc99fa55e654 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
#ifndef SLANG_CORE_STRING_SLICE_POOL_H
#define SLANG_CORE_STRING_SLICE_POOL_H

#include "slang-array-view.h"
#include "slang-dictionary.h"
#include "slang-list.h"
#include "slang-memory-arena.h"
#include "slang-string.h"

namespace Slang
{

/* Holds a unique set of slices.

Note that all slices (except kNullHandle) are stored with terminating zeros.

The default handles kNullHandle, kEmptyHandle can only be used on a StringSlicePool
initialized with the Style::Default. Not doing so will return an undefined result.

TODO(JS):
An argument could be made to make different classes, perhaps deriving from a base class
that exhibited the two behaviors. That doing so would make the default handles defined
for that class for example.

This is a little awkward in practice, because behavior of some methods need to change
(like adding a c string with nullptr, or clearing, as well as some other perhaps less necessary
optimizations). This could be achieved via virtual functions, but this all seems overkill.
*/
class StringSlicePool
{
public:
    typedef StringSlicePool ThisType;
    typedef uint32_t HandleIntegral;

    enum class Style
    {
        Default, ///< Default style - has default handles (like kNullHandle and kEmptyHandle)
        Empty,   ///< Empty style - has no handles by default. Using default handles will likely
                 ///< produce the wrong result.
    };

    enum class Handle : HandleIntegral;
    typedef UnownedStringSlice Slice;

    /// The following default handles *only* apply if constructed with the Style::Default

    /// Handle of 0 is null. If accessed will be returned as the empty string with nullptr the chars
    static const Handle kNullHandle = Handle(0);
    /// Handle of 1 is the empty string.
    static const Handle kEmptyHandle = Handle(1);

    static const Index kDefaultHandlesCount = 2;

    /// Returns the index of a slice, if contained, or -1 if not found
    Index findIndex(const Slice& slice) const;

    /// True if has the slice
    bool has(const Slice& slice) { return findIndex(slice) >= 0; }
    /// Add a slice
    Handle add(const Slice& slice);
    /// Add from a string
    Handle add(const char* chars);
    /// Add a StringRepresentation
    Handle add(StringRepresentation* string);
    /// Add a string
    Handle add(const String& string) { return add(string.getUnownedSlice()); }

    /// Add and get the result as a slice
    Slice addAndGetSlice(const Slice& slice) { return getSlice(add(slice)); }
    Slice addAndGetSlice(const char* chars) { return getSlice(add(chars)); }
    Slice addAndGetSlice(const String& string) { return getSlice(add(string)); }

    /// Returns true if found
    bool findOrAdd(const Slice& slice, Handle& outHandle);

    /// Empty contents
    void clear();

    /// Get the slice from the handle
    const UnownedStringSlice& getSlice(Handle handle) const { return m_slices[UInt(handle)]; }

    /// Get all the slices
    const List<UnownedStringSlice>& getSlices() const { return m_slices; }

    /// Get the number of slices
    Index getSlicesCount() const { return m_slices.getCount(); }

    /// Returns true if the handle is a default one. Only meaningful on a Style::Default.
    bool isDefaultHandle(Handle handle) const
    {
        SLANG_ASSERT(
            m_style == Style::Default &&
            // TODO(C++20), use bit_cast here
            HandleIntegral(handle) <= HandleIntegral(std::numeric_limits<Index>::max()));
        return Index(handle) < kDefaultHandlesCount;
    }

    /// Convert a handle to and index. (A handle is just an index!)
    static Index asIndex(Handle handle) { return Index(handle); }

    /// Get the style of the pool
    Style getStyle() const { return m_style; }

    /// Get all the added slices (does not have default slices, if there are any)
    ConstArrayView<UnownedStringSlice> getAdded() const;

    /// Get the index of the first added handle
    Index getFirstAddedIndex() const
    {
        return m_style == Style::Default ? kDefaultHandlesCount : 0;
    }

    /// Swap this with rhs
    void swapWith(ThisType& rhs);

    /// True if the pools are identical. Same style, same slices in the same order.
    bool operator==(const ThisType& rhs) const;

    /// Copy ctor
    StringSlicePool(const ThisType& rhs);
    /// Assignment
    void operator=(const ThisType& rhs);

    /// Ctor
    explicit StringSlicePool(Style style);

protected:
    void _set(const ThisType& rhs);

    Style m_style;
    List<UnownedStringSlice> m_slices;
    Dictionary<UnownedStringSlice, Handle> m_map;
    MemoryArena m_arena;
};

} // namespace Slang

#endif // SLANG_STRING_SLICE_POOL_H