#ifndef _POOLED_STRING_H
#define _POOLED_STRING_H

// std::string is quite general:
// - mutable
// - unlimited length
// - capacity can differ from size
// - can deallocate its dynamic memory
//
// Our use case, by contrast is immutable, bounded strings that live for the
// duration of the process.
//
// This gives us some room to have less memory overhead, especially on
// g++, whose implementation of std::string requires 32 bytes.
//
// Thus, we implement `PooledString`. It has a size of 16 bytes, and a small
// string optimization for strings <= 15 bytes. (We will separately teach
// AttributePair to encode Latin-character strings more efficiently, so that many
// strings of size 24 or less fit in 15 bytes.)
//
// If it needs to allocate memory, it does so from a shared pool. It is unable
// to free the memory once allocated.

// PooledString has one of three modes:
// - [126:127] = 00: small-string, length is in [120:125], lower 15 bytes are string
// - [126:127] = 10: pooled string, table is in bytes 1..3, offset in bytes 4..5, length in bytes 6..7
// - [126:127] = 11: pointer to std::string, pointer is in bytes 8..15
//
// Note that the pointer mode is not safe to be stored. It exists just to allow
// lookups in the AttributePair map before deciding to allocate a string.

#include <cstdint>
#include <vector>
#include <string>
#include <protozero/data_view.hpp>

namespace PooledStringNS {
  class PooledString {
    public:
      // Create a short string or heap string, long-lived.
      PooledString(const std::string& str);


      // Wrap a protozero::data_view - only valid so long as the
      // data_view that is pointed to is valid; call ensureStringIsOwned
      // if you need to persist the string.
      PooledString(const protozero::data_view* str);
      size_t size() const;
      bool operator<(const PooledString& other) const;
      bool operator==(const PooledString& other) const;
      bool operator!=(const PooledString& other) const;
      std::string toString() const;
      const char* data() const;
      void ensureStringIsOwned();

    private:
      // 0..3 is index into table, 4..5 is offset, 6..7 is length
      uint8_t storage[16];
  };
}

using PooledString = PooledStringNS::PooledString;

#endif
