// SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once #include #include #include #include #include typedef int32_t token_t; class Prefix; // typedef std::shared_ptr PrefixPtr; class Prefix { public: token_t Token; Prefix *Parent; Prefix(token_t token = 0 /* blank */, Prefix *parent = nullptr) : Token(token), Parent(parent) {} std::vector ToList() const; size_t size() const; }; ///// Borrowed from Boost libraries template void hash_combine(size_t & seed, T const& v) { seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } ///// namespace std { template<> struct hash { size_t operator()(const Prefix *p) const noexcept { size_t seed = 0; while (p) { if (p->Token != 0) { hash_combine(seed, p->Token); } p = p->Parent; } return seed; } }; template<> struct hash> { size_t operator()(const tuple &t) const noexcept { size_t seed = 0; hash_combine(seed, get<0>(t)); hash_combine(seed, get<1>(t)); return seed; } }; template<> struct equal_to { bool operator()(const Prefix *a, const Prefix *b) const noexcept { while (a != nullptr && b != nullptr) { if (a->Token != b->Token) { return false; } a = a->Parent; b = b->Parent; } // If one chain is shorter than the other return a == b; } }; } inline size_t Prefix::size() const { size_t ret = 0; auto p = this; while (p != nullptr) { ret += 1; p = p->Parent; } return ret; } class PrefixAllocator { public: PrefixAllocator() = default; ~PrefixAllocator(); template Prefix *GetPrefix(Args&& ...ctorArgs); private: void AllocateNextBuffer(); std::list m_buffers; size_t m_allocSize = 0; size_t m_currOff = 0; }; inline PrefixAllocator::~PrefixAllocator() { for (auto p : m_buffers) { // Prefix is a POD, and are allocated without initializing // to prevent redundant work upfront // delete[] p; free(p); } } inline void PrefixAllocator::AllocateNextBuffer() { size_t nextSize = m_allocSize == 0 ? 1000 : 2 * m_allocSize; // Using malloc here to prevent the ctor of Prefix being called for each item. // Instead, the ctor will be called upon first access using GetPrefix auto pBuff = reinterpret_cast(malloc(sizeof(Prefix) * nextSize)); m_buffers.push_back(pBuff); m_allocSize = nextSize; m_currOff = 0; } template Prefix *PrefixAllocator::GetPrefix(Args&& ...ctorArgs) { if (m_currOff == m_allocSize) { AllocateNextBuffer(); } auto buff = m_buffers.back() + m_currOff; auto ret = new (buff) Prefix(std::forward(ctorArgs)...); ++m_currOff; return ret; }