odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

string.h (13787B)


      1 // This file is part of AsmJit project <https://asmjit.com>
      2 //
      3 // See <asmjit/core.h> or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 #ifndef ASMJIT_CORE_STRING_H_INCLUDED
      7 #define ASMJIT_CORE_STRING_H_INCLUDED
      8 
      9 #include "../core/span.h"
     10 #include "../core/support.h"
     11 
     12 ASMJIT_BEGIN_NAMESPACE
     13 
     14 //! \addtogroup asmjit_utilities
     15 //! \{
     16 
     17 //! Format flags used by \ref String API.
     18 enum class StringFormatFlags : uint32_t {
     19   //! No flags.
     20   kNone = 0x00000000u,
     21   //! Show sign.
     22   kShowSign = 0x00000001u,
     23   //! Show space.
     24   kShowSpace = 0x00000002u,
     25   //! Alternate form (use 0x when formatting HEX number).
     26   kAlternate = 0x00000004u,
     27   //! The input is signed.
     28   kSigned = 0x80000000u
     29 };
     30 ASMJIT_DEFINE_ENUM_FLAGS(StringFormatFlags)
     31 
     32 //! Fixed string - only useful for strings that would never exceed `N - 1` characters; always null-terminated.
     33 template<size_t N>
     34 union FixedString {
     35   //! \name Constants
     36   //! \{
     37 
     38   static inline constexpr uint32_t kNumUInt32Words = uint32_t((N + sizeof(uint32_t) - 1) / sizeof(uint32_t));
     39 
     40   //! \}
     41 
     42   //! \name Members
     43   //! \{
     44 
     45   char str[kNumUInt32Words * sizeof(uint32_t)];
     46   uint32_t u32[kNumUInt32Words];
     47 
     48   //! \}
     49 
     50   //! \name Utilities
     51   //! \{
     52 
     53   [[nodiscard]]
     54   inline bool equals(const char* other) const noexcept { return strcmp(str, other) == 0; }
     55 
     56   //! \}
     57 };
     58 
     59 //! A simple non-reference counted string that uses small string optimization (SSO).
     60 //!
     61 //! This string has 3 allocation possibilities:
     62 //!
     63 //!   1. Small    - embedded buffer is used for up to `kSSOCapacity` characters. This should handle most small
     64 //!                 strings and thus avoid dynamic memory allocation for most use-cases.
     65 //!
     66 //!   2. Large    - string that doesn't fit into an embedded buffer (or string that was truncated from a larger
     67 //!                 buffer) and is owned by AsmJit. When you destroy the string AsmJit would automatically
     68 //!                 release the large buffer.
     69 //!
     70 //!   3. External - like Large (2), however, the large buffer is not owned by AsmJit and won't be released when
     71 //!                 the string is destroyed or reallocated. This is mostly useful for working with larger temporary
     72 //!                 strings allocated on stack or with immutable strings.
     73 class String {
     74 public:
     75   ASMJIT_NONCOPYABLE(String)
     76 
     77   //! String operation.
     78   enum class ModifyOp : uint32_t {
     79     //! Assignment - a new content replaces the current one.
     80     kAssign = 0,
     81     //! Append - a new content is appended to the string.
     82     kAppend = 1
     83   };
     84 
     85   //! \cond INTERNAL
     86   static inline constexpr uint32_t kLayoutSize = 32;
     87   static inline constexpr uint32_t kSSOCapacity = kLayoutSize - 2;
     88 
     89   //! Large string (owned by String).
     90   static inline constexpr uint8_t kTypeLarge = 0x1Fu;
     91   //! External string (arena allocated or not owned by String).
     92   static inline constexpr uint8_t kTypeExternal = 0x20u;
     93 
     94   [[nodiscard]]
     95   static ASMJIT_INLINE_NODEBUG bool is_external(uint8_t type) noexcept { return type == kTypeExternal; }
     96 
     97   [[nodiscard]]
     98   static ASMJIT_INLINE_NODEBUG bool is_large_or_external(uint8_t type) noexcept { return type >= kTypeLarge; }
     99 
    100   union Raw {
    101     uint8_t u8[kLayoutSize];
    102     uint64_t u64[kLayoutSize / sizeof(uint64_t)];
    103     uintptr_t uptr[kLayoutSize / sizeof(uintptr_t)];
    104   };
    105 
    106   struct Small {
    107     uint8_t type;
    108     char data[kSSOCapacity + 1u];
    109   };
    110 
    111   struct Large {
    112     uint8_t type;
    113     uint8_t reserved[sizeof(uintptr_t) - 1];
    114     size_t size;
    115     size_t capacity;
    116     char* data;
    117   };
    118 
    119   union {
    120     uint8_t _type;
    121     Raw _raw;
    122     Small _small;
    123     Large _large;
    124   };
    125   //! \endcond
    126 
    127   //! \name Construction & Destruction
    128   //! \{
    129 
    130   //! Creates a default-initialized string if zero length.
    131   ASMJIT_INLINE_NODEBUG String() noexcept
    132     : _small {} {}
    133 
    134   //! Creates a string that takes ownership of the content of the `other` string.
    135   ASMJIT_INLINE_NODEBUG String(String&& other) noexcept {
    136     _raw = other._raw;
    137     other._reset_internal();
    138   }
    139 
    140   ASMJIT_INLINE_NODEBUG ~String() noexcept {
    141     reset();
    142   }
    143 
    144   //! Reset the string into a construction state.
    145   ASMJIT_API Error reset() noexcept;
    146 
    147   //! \}
    148 
    149   //! \name Overloaded Operators
    150   //! \{
    151 
    152   inline String& operator=(String&& other) noexcept {
    153     swap(other);
    154     other.reset();
    155     return *this;
    156   }
    157 
    158   [[nodiscard]]
    159   ASMJIT_INLINE_NODEBUG bool operator==(const char* other) const noexcept { return  equals(other); }
    160 
    161   [[nodiscard]]
    162   ASMJIT_INLINE_NODEBUG bool operator!=(const char* other) const noexcept { return !equals(other); }
    163 
    164   [[nodiscard]]
    165   ASMJIT_INLINE_NODEBUG bool operator==(const String& other) const noexcept { return  equals(other); }
    166 
    167   [[nodiscard]]
    168   ASMJIT_INLINE_NODEBUG bool operator!=(const String& other) const noexcept { return !equals(other); }
    169 
    170   //! \}
    171 
    172   //! \name Accessors
    173   //! \{
    174 
    175   [[nodiscard]]
    176   ASMJIT_INLINE_NODEBUG bool is_external() const noexcept { return is_external(_type); }
    177 
    178   [[nodiscard]]
    179   ASMJIT_INLINE_NODEBUG bool is_large_or_external() const noexcept { return is_large_or_external(_type); }
    180 
    181   //! Tests whether the string is empty.
    182   [[nodiscard]]
    183   ASMJIT_INLINE_NODEBUG bool is_empty() const noexcept { return size() == 0; }
    184 
    185   //! Returns the size of the string.
    186   [[nodiscard]]
    187   ASMJIT_INLINE_NODEBUG size_t size() const noexcept { return is_large_or_external() ? size_t(_large.size) : size_t(_type); }
    188 
    189   //! Returns the capacity of the string.
    190   [[nodiscard]]
    191   ASMJIT_INLINE_NODEBUG size_t capacity() const noexcept { return is_large_or_external() ? _large.capacity : size_t(kSSOCapacity); }
    192 
    193   //! Returns the data of the string.
    194   [[nodiscard]]
    195   ASMJIT_INLINE_NODEBUG char* data() noexcept { return is_large_or_external() ? _large.data : _small.data; }
    196 
    197   //! \overload
    198   [[nodiscard]]
    199   ASMJIT_INLINE_NODEBUG const char* data() const noexcept { return is_large_or_external() ? _large.data : _small.data; }
    200 
    201   [[nodiscard]]
    202   ASMJIT_INLINE_NODEBUG char* begin() noexcept { return data(); }
    203 
    204   [[nodiscard]]
    205   ASMJIT_INLINE_NODEBUG const char* begin() const noexcept { return data(); }
    206 
    207   [[nodiscard]]
    208   ASMJIT_INLINE_NODEBUG char* end() noexcept { return data() + size(); }
    209 
    210   [[nodiscard]]
    211   ASMJIT_INLINE_NODEBUG const char* end() const noexcept { return data() + size(); }
    212 
    213   [[nodiscard]]
    214   ASMJIT_INLINE_NODEBUG Span<char> as_span() noexcept { return Span<char>(data(), size()); }
    215 
    216   [[nodiscard]]
    217   ASMJIT_INLINE_NODEBUG Span<const char> as_span() const noexcept { return Span<const char>(data(), size()); }
    218 
    219   //! \}
    220 
    221   //! \name String Operations
    222   //! \{
    223 
    224   //! Swaps the content of this string with `other`.
    225   ASMJIT_INLINE_NODEBUG void swap(String& other) noexcept {
    226     std::swap(_raw, other._raw);
    227   }
    228 
    229   //! Clears the content of the string.
    230   ASMJIT_API Error clear() noexcept;
    231 
    232   [[nodiscard]]
    233   ASMJIT_API char* prepare(ModifyOp op, size_t size) noexcept;
    234 
    235   //! \cond INTERNAL
    236   ASMJIT_API Error _op_string(ModifyOp op, const char* str, size_t size = SIZE_MAX) noexcept;
    237   ASMJIT_API Error _op_char(ModifyOp op, char c) noexcept;
    238   ASMJIT_API Error _op_chars(ModifyOp op, char c, size_t n) noexcept;
    239   ASMJIT_API Error _op_number(ModifyOp op, uint64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept;
    240   ASMJIT_API Error _op_hex(ModifyOp op, const void* data, size_t size, char separator = '\0') noexcept;
    241   ASMJIT_API Error _op_format(ModifyOp op, const char* fmt, ...) noexcept;
    242   ASMJIT_API Error _op_vformat(ModifyOp op, const char* fmt, va_list ap) noexcept;
    243   //! \endcond
    244 
    245   //! Replaces the current of the string with `data` of the given `size`.
    246   //!
    247   //! Null terminated strings can set `size` to `SIZE_MAX`.
    248   ASMJIT_API Error assign(const char* data, size_t size = SIZE_MAX) noexcept;
    249 
    250   //! Appends the given `span` to the string.
    251   ASMJIT_INLINE_NODEBUG Error assign(Span<const char> span) noexcept {
    252     return _op_string(ModifyOp::kAssign, span.data(), span.size());
    253   }
    254 
    255   //! Replaces the current of the string with `other` string.
    256   ASMJIT_INLINE_NODEBUG Error assign(const String& other) noexcept {
    257     return assign(other.data(), other.size());
    258   }
    259 
    260   //! Replaces the current of the string by a single `c` character.
    261   ASMJIT_INLINE_NODEBUG Error assign(char c) noexcept {
    262     return _op_char(ModifyOp::kAssign, c);
    263   }
    264 
    265   //! Replaces the current of the string by a `c` character, repeated `n` times.
    266   ASMJIT_INLINE_NODEBUG Error assign_chars(char c, size_t n) noexcept {
    267     return _op_chars(ModifyOp::kAssign, c, n);
    268   }
    269 
    270   //! Replaces the current of the string by a formatted integer `i` (signed).
    271   ASMJIT_INLINE_NODEBUG Error assign_int(int64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept {
    272     return _op_number(ModifyOp::kAssign, uint64_t(i), base, width, flags | StringFormatFlags::kSigned);
    273   }
    274 
    275   //! Replaces the current of the string by a formatted integer `i` (unsigned).
    276   ASMJIT_INLINE_NODEBUG Error assign_uint(uint64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept {
    277     return _op_number(ModifyOp::kAssign, i, base, width, flags);
    278   }
    279 
    280   //! Replaces the current of the string by the given `data` converted to a HEX string.
    281   ASMJIT_INLINE_NODEBUG Error assign_hex(const void* data, size_t size, char separator = '\0') noexcept {
    282     return _op_hex(ModifyOp::kAssign, data, size, separator);
    283   }
    284 
    285   //! Replaces the current of the string by a formatted string `fmt`.
    286   template<typename... Args>
    287   ASMJIT_INLINE_NODEBUG Error assign_format(const char* fmt, Args&&... args) noexcept {
    288     return _op_format(ModifyOp::kAssign, fmt, std::forward<Args>(args)...);
    289   }
    290 
    291   //! Replaces the current of the string by a formatted string `fmt` (va_list version).
    292   ASMJIT_INLINE_NODEBUG Error assign_vformat(const char* fmt, va_list ap) noexcept {
    293     return _op_vformat(ModifyOp::kAssign, fmt, ap);
    294   }
    295 
    296   //! Appends `str` having the given size `size` to the string.
    297   //!
    298   //! Null terminated strings can set `size` to `SIZE_MAX`.
    299   ASMJIT_INLINE_NODEBUG Error append(const char* str, size_t size = SIZE_MAX) noexcept {
    300     return _op_string(ModifyOp::kAppend, str, size);
    301   }
    302 
    303   //! Appends the given `span` to the string.
    304   ASMJIT_INLINE_NODEBUG Error append(Span<const char> span) noexcept {
    305     return _op_string(ModifyOp::kAppend, span.data(), span.size());
    306   }
    307 
    308   //! Appends `other` string to this string.
    309   ASMJIT_INLINE_NODEBUG Error append(const String& other) noexcept {
    310     return append(other.data(), other.size());
    311   }
    312 
    313   //! Appends a single `c` character.
    314   ASMJIT_INLINE_NODEBUG Error append(char c) noexcept {
    315     return _op_char(ModifyOp::kAppend, c);
    316   }
    317 
    318   //! Appends `c` character repeated `n` times.
    319   ASMJIT_INLINE_NODEBUG Error append_chars(char c, size_t n) noexcept {
    320     return _op_chars(ModifyOp::kAppend, c, n);
    321   }
    322 
    323   //! Appends a formatted integer `i` (signed).
    324   ASMJIT_INLINE_NODEBUG Error append_int(int64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept {
    325     return _op_number(ModifyOp::kAppend, uint64_t(i), base, width, flags | StringFormatFlags::kSigned);
    326   }
    327 
    328   //! Appends a formatted integer `i` (unsigned).
    329   ASMJIT_INLINE_NODEBUG Error append_uint(uint64_t i, uint32_t base = 0, size_t width = 0, StringFormatFlags flags = StringFormatFlags::kNone) noexcept {
    330     return _op_number(ModifyOp::kAppend, i, base, width, flags);
    331   }
    332 
    333   //! Appends the given `data` converted to a HEX string.
    334   ASMJIT_INLINE_NODEBUG Error append_hex(const void* data, size_t size, char separator = '\0') noexcept {
    335     return _op_hex(ModifyOp::kAppend, data, size, separator);
    336   }
    337 
    338   //! Appends a formatted string `fmt` with `args`.
    339   template<typename... Args>
    340   ASMJIT_INLINE_NODEBUG Error append_format(const char* fmt, Args&&... args) noexcept {
    341     return _op_format(ModifyOp::kAppend, fmt, std::forward<Args>(args)...);
    342   }
    343 
    344   //! Appends a formatted string `fmt` (va_list version).
    345   ASMJIT_INLINE_NODEBUG Error append_vformat(const char* fmt, va_list ap) noexcept {
    346     return _op_vformat(ModifyOp::kAppend, fmt, ap);
    347   }
    348 
    349   ASMJIT_API Error pad_end(size_t n, char c = ' ') noexcept;
    350 
    351   //! Truncate the string length into `new_size`.
    352   ASMJIT_API Error truncate(size_t new_size) noexcept;
    353 
    354   [[nodiscard]]
    355   ASMJIT_API bool equals(const char* other, size_t size = SIZE_MAX) const noexcept;
    356 
    357   [[nodiscard]]
    358   ASMJIT_INLINE_NODEBUG bool equals(const String& other) const noexcept { return equals(other.data(), other.size()); }
    359 
    360   //! \}
    361 
    362   //! \name Internal Functions
    363   //! \{
    364 
    365   //! Resets string to embedded and makes it empty (zero length, zero first char)
    366   //!
    367   //! \note This is always called internally after an external buffer was released as it zeroes all bytes
    368   //! used by String's embedded storage.
    369   inline void _reset_internal() noexcept {
    370     for (size_t i = 0; i < ASMJIT_ARRAY_SIZE(_raw.uptr); i++) {
    371       _raw.uptr[i] = 0;
    372     }
    373   }
    374 
    375   inline void _set_size(size_t new_size) noexcept {
    376     if (is_large_or_external()) {
    377       _large.size = new_size;
    378     }
    379     else {
    380       _small.type = uint8_t(new_size);
    381     }
    382   }
    383 
    384   //! \}
    385 };
    386 
    387 //! Temporary string builder, has statically allocated `N` bytes.
    388 template<size_t N>
    389 class StringTmp : public String {
    390 public:
    391   ASMJIT_NONCOPYABLE(StringTmp)
    392 
    393   //! Embedded data.
    394   char _embedded_data[Support::align_up(N + 1, sizeof(size_t))];
    395 
    396   //! \name Construction & Destruction
    397   //! \{
    398 
    399   ASMJIT_INLINE_NODEBUG StringTmp() noexcept {
    400     _reset_to_temporary();
    401   }
    402 
    403   inline void _reset_to_temporary() noexcept {
    404     _large.type = kTypeExternal;
    405     _large.capacity = ASMJIT_ARRAY_SIZE(_embedded_data) - 1;
    406     _large.data = _embedded_data;
    407     _embedded_data[0] = '\0';
    408   }
    409 
    410   //! \}
    411 };
    412 
    413 //! \}
    414 
    415 ASMJIT_END_NAMESPACE
    416 
    417 #endif // ASMJIT_CORE_STRING_H_INCLUDED