Faster decoder
diff --git a/docs/performance.rst b/docs/performance.rst
index 4cf000a..66e9fe6 100644
--- a/docs/performance.rst
+++ b/docs/performance.rst
@@ -11,9 +11,10 @@
 If you know that your input does not use JSON5 extension, then this library is probably not what you need.
 
 * Dataset: https://github.com/zemirco/sf-city-lots-json
-* CPU: Core i7-3770 @ 3.40GHz
-* :func:`pyjson5.decode`: **4.58** s ± 68.6 ms per loop *(lower is better)*
-* :func:`json.loads`: **3.27** s ± 27.7 ms per loop
+* Version: Python 3.7.5 (default, Oct 27 2019, 15:43:29)
+* CPU: AMD Ryzen 7 2700 @ 3.7GHz
+* :func:`pyjson5.decode`: **3.45** s ± 15.2 ms per loop per loop per loop *(lower is better)*
+* :func:`json.loads`: **2.64** s ± 12.7 ms ms per loop
 * The decoder works correcty: ``json.loads(content) == pyjson5.loads(content)``
 
 
@@ -25,10 +26,9 @@
 The output is always ASCII regardless if you call :func:`pyjson5.encode` or :func:`pyjson5.encode_bytes`.
 
 * Dataset: https://github.com/zemirco/sf-city-lots-json
-* CPU: Core i7-3770 @ 3.40GHz
-* :func:`pyjson5.encode`: **8.54** s ± 29.3 ms per loop *(lower is better)*
-* :func:`json.dumps`: **4.68** s ± 20.4 ms per loop
-* :func:`json.dumps` + :func:`xml.sax.saxutils.escape`: **5.02** s ± 141 ms per loop
+* Version: Python 3.7.5 (default, Oct 27 2019, 15:43:29)
+* CPU: AMD Ryzen 7 2700 @ 3.7GHz
+* :func:`pyjson5.encode`: **3.42** s ± 11.8 ms per loop *(lower is better)*
+* :func:`json.dumps`: **3.77** s ± 14.4 ms per loop
+* :func:`json.dumps` + :func:`xml.sax.saxutils.escape`: **4.02** s ± 51.6 ms per loop
 * The encoder works correcty: ``obj == json.loads(pyjson5.encode(obj, floatformat='%.16e'))``
-
-Unless you need the advanced settings in :class:`pyjson5.Options`, most most likely don't benefit from using this library as an encoder.
diff --git a/make_unicode_categories.py b/make_unicode_categories.py
index 4cc1b16..bffd073 100755
--- a/make_unicode_categories.py
+++ b/make_unicode_categories.py
@@ -24,6 +24,7 @@
         'lo': IdentifierStart,
         'lt': IdentifierStart,
         'lu': IdentifierStart,
+        'nl': IdentifierStart,
 
         'mc': IdentifierPart,
         'mn': IdentifierPart,
@@ -98,7 +99,7 @@
     print(file=output_file)
 
     print('    std::uint16_t plane_idx = std::uint16_t(codepoint / 0x10000);', file=output_file)
-    print('    if (plane_idx > 16) return 0;', file=output_file)
+    print('    if (__builtin_expect(plane_idx > 16, false)) return 1;', file=output_file)
     print('    std::uint16_t datum_idx = std::uint16_t(codepoint & 0xffff);', file=output_file)
     print('    const std::uint8_t *plane = planes[plane_idx];', file=output_file)
     print('    return (plane[datum_idx / 4] >> (2 * (datum_idx % 4))) % 4;', file=output_file)
diff --git a/src/_decoder.pyx b/src/_decoder.pyx
index 155080b..f5ca865 100644
--- a/src/_decoder.pyx
+++ b/src/_decoder.pyx
@@ -187,7 +187,7 @@
 cdef object _decode_string_sub(ReaderRef reader, uint32_t delim,
                                Py_ssize_t start, uint32_t c0):
     cdef int32_t c1
-    cdef std_vector[uint32_t] buf
+    cdef StackHeapString[uint32_t] buf
 
     while True:
         if c0 == delim:
@@ -240,7 +240,7 @@
     return result
 
 
-cdef object _decode_number_leading_zero(ReaderRef reader, std_vector[char] &buf,
+cdef object _decode_number_leading_zero(ReaderRef reader, StackHeapString[char] &buf,
                                         int32_t *c_in_out):
     cdef uint32_t c0
     cdef int32_t c1
@@ -308,7 +308,7 @@
         return 0
 
 
-cdef object _decode_number_any(ReaderRef reader, std_vector[char] &buf,
+cdef object _decode_number_any(ReaderRef reader, StackHeapString[char] &buf,
                                int32_t *c_in_out):
     cdef uint32_t c0
     cdef int32_t c1
@@ -350,7 +350,7 @@
     cdef uint32_t c0
     cdef int32_t c1
     cdef Py_ssize_t start
-    cdef std_vector[char] buf
+    cdef StackHeapString[char] buf
 
     c1 = c_in_out[0]
     c0 = cast_to_uint32(c1)
@@ -369,8 +369,6 @@
             _accept_string(reader, b'aN')
             c_in_out[0] = NO_EXTRA_DATA
             return CONST_POS_NAN
-
-        buf.reserve(16)
     elif c0 == b'-':
         start = _reader_tell(reader)
         if expect(not _reader_good(reader), False):
@@ -386,10 +384,7 @@
             c_in_out[0] = NO_EXTRA_DATA
             return CONST_NEG_NAN
 
-        buf.reserve(16)
         buf.push_back(b'-')
-    else:
-        buf.reserve(16)
 
     if c0 == b'0':
         return _decode_number_leading_zero(reader, buf, c_in_out)
@@ -449,7 +444,7 @@
     cdef int32_t c0
     cdef uint32_t c1
     cdef Py_ssize_t start
-    cdef std_vector[uint32_t] buf
+    cdef StackHeapString[uint32_t] buf
 
     start = _reader_tell(reader)
 
diff --git a/src/_encoder.pyx b/src/_encoder.pyx
index 2137cc6..c2f83a9 100644
--- a/src/_encoder.pyx
+++ b/src/_encoder.pyx
@@ -399,7 +399,7 @@
     )
 
     if not callable(cb):
-        raise TypeError(f'type(cb)=={type(cb)!r} is callable')
+        raise TypeError(f'type(cb)=={type(cb)!r} is not callable')
 
     _encode(writer.base, data)
 
@@ -418,7 +418,7 @@
     )
 
     if not callable(cb):
-        raise TypeError(f'type(cb)=={type(cb)!r} is callable')
+        raise TypeError(f'type(cb)=={type(cb)!r} is not callable')
 
     _encode(writer.base, data)
 
diff --git a/src/_imports.pyx b/src/_imports.pyx
index ee37d0a..790ad4e 100644
--- a/src/_imports.pyx
+++ b/src/_imports.pyx
@@ -17,7 +17,6 @@
 from cpython.type cimport PyType_Check
 from cpython.unicode cimport PyUnicode_Check
 from libcpp cimport bool as boolean
-from libcpp.vector cimport vector as std_vector
 
 
 cdef extern from '<cstddef>' namespace 'std' nogil:
@@ -96,6 +95,13 @@
     unsigned unicode_cat_of(uint32_t codepoint)
 
 
+cdef extern from 'src/_stack_heap_string.hpp' namespace 'JSON5EncoderCpp' nogil:
+    cdef cppclass StackHeapString [T]:
+        const T *data()
+        Py_ssize_t size()
+        boolean push_back(T codepoint) except False
+
+
 cdef extern from 'Python.h':
     enum:
         PyUnicode_WCHAR_KIND
diff --git a/src/_stack_heap_string.hpp b/src/_stack_heap_string.hpp
new file mode 100644
index 0000000..52bd4af
--- /dev/null
+++ b/src/_stack_heap_string.hpp
@@ -0,0 +1,90 @@
+#ifndef JSON5EncoderCpp_StackHeapString
+#define JSON5EncoderCpp_StackHeapString
+
+#include <cstdint>
+#include <cstring>
+#include <Python.h>
+
+
+namespace JSON5EncoderCpp {
+inline namespace {
+
+static constexpr Py_ssize_t StackHeapStringStackSize = 64;
+static constexpr Py_ssize_t StackHeapStringHeapSize = 256;
+static constexpr Py_ssize_t StackHeapStringHeapFactor = 4;
+
+
+template <class T>
+class StackHeapString {
+    StackHeapString(const StackHeapString&) = delete;
+    StackHeapString(StackHeapString&&) = delete;
+    StackHeapString &operator =(const StackHeapString&) = delete;
+    StackHeapString &operator =(StackHeapString&&) = delete;
+
+public:
+    StackHeapString() = default;
+
+    ~StackHeapString() {
+        if (m_heap != nullptr) {
+            PyMem_RawFree(m_heap);
+        }
+    }
+
+    const T *data() const& {
+        if (__builtin_expect(m_heap == nullptr, true)) {
+            return m_stack;
+        } else {
+            return m_heap;
+        }
+    }
+
+    Py_ssize_t size() const& {
+        return m_size;
+    }
+
+    bool push_back(T c) {
+        if (__builtin_expect(m_left == 0, false)) {
+            if (m_heap == nullptr) {
+                void *new_ptr = PyMem_RawMalloc(sizeof(T) * StackHeapStringHeapSize);
+                if (new_ptr == nullptr) {
+                    PyErr_NoMemory();
+                    return false;
+                }
+
+                m_heap = reinterpret_cast<T*>(new_ptr);
+                m_left = StackHeapStringHeapSize - StackHeapStringStackSize;
+                std::memcpy(m_heap, m_stack, sizeof(T) * StackHeapStringStackSize);
+            } else {
+                void *new_ptr = PyMem_RawRealloc(m_heap, sizeof(T) * (m_size * StackHeapStringHeapFactor));
+                if (new_ptr == nullptr) {
+                    PyErr_NoMemory();
+                    return false;
+                }
+
+                m_heap = reinterpret_cast<T*>(new_ptr);
+                m_left = m_size * (StackHeapStringHeapFactor - 1);
+            }
+        }
+
+        if (__builtin_expect(m_heap == nullptr, true)) {
+            m_stack[m_size] = c;
+        } else {
+            m_heap[m_size] = c;
+        }
+
+        ++m_size;
+        --m_left;
+        return true;
+    }
+
+private:
+    Py_ssize_t m_size = 0;
+    Py_ssize_t m_left = StackHeapStringStackSize;
+    T *m_heap = nullptr;
+    T m_stack[StackHeapStringStackSize];
+};
+
+}
+}
+
+#endif  // ifndef JSON5EncoderCpp_StackHeapString
diff --git a/src/_unicode.pyx b/src/_unicode.pyx
index b2df23a..4aefc03 100644
--- a/src/_unicode.pyx
+++ b/src/_unicode.pyx
@@ -9,7 +9,7 @@
 
 cdef boolean _is_ws_zs(uint32_t c) nogil:
     # https://spec.json5.org/#white-space
-    return (unicode_cat_of(c) == 1) or (c == NO_EXTRA_DATA)
+    return unicode_cat_of(c) == 1
 
 cdef boolean _is_identifier_start(uint32_t c) nogil:
     # https://www.ecma-international.org/ecma-262/5.1/#sec-7.6
diff --git a/src/_unicode_cat_of.hpp b/src/_unicode_cat_of.hpp
index 8896bdd..9a22be4 100644
--- a/src/_unicode_cat_of.hpp
+++ b/src/_unicode_cat_of.hpp
@@ -104,7 +104,7 @@
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x82u, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xa9u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x2au, 0x00u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
-        0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x2au, 0x00u, 0xa8u, 0xaau, 0x02u, 0x00u, 
+        0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x2au, 0xa0u, 0xaau, 0xaau, 0x02u, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xa2u, 0xfau, 0x03u, 0x00u, 0x00u, 0xaau, 0xaau, 0xaau, 0xaau, 0xfau, 0x03u, 0x00u, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xfau, 0x00u, 0x00u, 0x00u, 0xaau, 0xaau, 0xaau, 0xa2u, 0xf2u, 0x00u, 0x00u, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xffu, 0xffu, 0xffu, 
@@ -146,8 +146,8 @@
         0x00u, 0x00u, 0x00u, 0x00u, 0xaau, 0xaau, 0xaau, 0x02u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0xffu, 0xffu, 0xffu, 0x03u, 0x0cu, 0xfcu, 0xffu, 0xffu, 0x03u, 0x00u, 0x00u, 0x00u, 
         0x20u, 0x80u, 0xa0u, 0xaau, 0xaau, 0x08u, 0xa8u, 0x0au, 0x00u, 0x22u, 0xa2u, 0x8au, 0xaau, 0xaau, 0x0au, 0xaau, 
-        0x00u, 0xa8u, 0x0au, 0x20u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
-        0x80u, 0x02u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
+        0x00u, 0xa8u, 0x0au, 0x20u, 0x00u, 0x00u, 0x00u, 0x00u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
+        0xaau, 0xaau, 0x02u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
@@ -205,7 +205,7 @@
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
-        0x01u, 0x28u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0xf0u, 0xffu, 0xa8u, 0x0au, 0x80u, 0x02u, 
+        0x01u, 0xa8u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0xa8u, 0xaau, 0xfau, 0xffu, 0xa8u, 0x0au, 0xaau, 0x02u, 
         0xa8u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x2au, 0x3cu, 0xa8u, 0xa8u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x2au, 0xaau, 
@@ -680,7 +680,7 @@
         0xaau, 0xaau, 0xaau, 0x02u, 0xaau, 0xaau, 0xaau, 0xaau, 0xffu, 0xffu, 0xafu, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xeau, 0x00u, 0xffu, 0xffu, 0x8fu, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xfau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
-        0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x0au, 0x00u, 0x00u, 0x0fu, 0x00u, 0x00u, 0x00u, 
+        0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x0fu, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x80u, 0xaau, 0xaau, 0xa0u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0x82u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
@@ -1045,7 +1045,7 @@
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x2au, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
-        0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
+        0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x02u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x0cu, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
@@ -1053,9 +1053,9 @@
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x02u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0x02u, 0x00u, 0x00u, 0x00u, 0x03u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x00u, 0x00u, 0x00u, 0xa8u, 0xaau, 0xaau, 0xaau, 0xaau, 
-        0xa2u, 0xaau, 0x0au, 0x00u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xfau, 0x3fu, 0x00u, 
+        0xaau, 0xaau, 0x2au, 0x00u, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xfau, 0x3fu, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x0au, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
-        0xaau, 0x00u, 0xaau, 0xaau, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
+        0xaau, 0x00u, 0xaau, 0xaau, 0xa8u, 0x0au, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x0au, 0xffu, 0xffu, 0x0fu, 0x00u, 0xaau, 0xaau, 0xaau, 0xaau, 
@@ -1184,8 +1184,8 @@
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x0au, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
         0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
-        0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
-        0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 
+        0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
+        0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0x2au, 0x00u, 0x00u, 0x00u, 0x00u, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
         0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 0xaau, 
@@ -4127,7 +4127,7 @@
     };
 
     std::uint16_t plane_idx = std::uint16_t(codepoint / 0x10000);
-    if (plane_idx > 16) return 0;
+    if (__builtin_expect(plane_idx > 16, false)) return 1;
     std::uint16_t datum_idx = std::uint16_t(codepoint & 0xffff);
     const std::uint8_t *plane = planes[plane_idx];
     return (plane[datum_idx / 4] >> (2 * (datum_idx % 4))) % 4;