// Will be deprecated. Use library/any instead.
//
// Очень низкоуровневая библиотека для построения деревьев данных в памяти
// Используется парсером JSON'а для возвращения результата
// Поддерживает расширение типов нод пользовательскими
// Ответственность за выделение и освобождение памяти под деревья и сопутствующие данные
// полностью на пользователе, но этот процесс может быть упрощён при помощи менеджеров
{
    Как переписывать со старого djson ридера на dnodevalue:
    1. JSON_* -> NODE_*
    2. _Type -> Type_
    3. AnsiString -> TAnsiView
    4. TJson -> TNodeObject, TJsonArray -> TNodeList
}
{$MODE FPC}
{$MODESWITCH OUT}
{$MODESWITCH RESULT}
{$MODESWITCH DEFAULTPARAMETERS}
{$MODESWITCH ADVANCEDRECORDS}
unit dnode;

interface

uses
  dmemory,
  //dstrings,
  dansiview,
  dstringpool,
  dchainedallocator,
  dhashfuncs;

const
  NODE_NULL     = 0;
  NODE_INT      = 1;
  NODE_FLOAT    = 2;
  NODE_STRING   = 3;
  NODE_BOOLEAN  = 4;
  NODE_ARRAY    = 5;
  NODE_OBJECT   = 6;
  NODE_LIST     = 7;

  // Declare your own types as NODE_USERDATA+i, 0<=i<=127
  NODE_USERDATA = 128;

type
PNodeArray = ^TNodeArray;
PNodeValue = ^TNodeValue;
PNodeItem = ^TNodeItem;
PPNodeItem = ^PNodeItem;
PNodeObject = ^TNodeObject;
//PNodeList = ^TNodeList;
PNodeManagerStatic = ^TNodeManagerStatic;

TNodeInt = Int64;
TNodeFloat = Double;
TNodeString = TAnsiView;
TNodeHash = THash32;

TNodeHashedString = record
  S: TNodeString;
  H: TNodeHash;
end;

TRawNodeValueA = record
  _Type: Word;
  Count: Word;
end;

TRawNodeValue = record
  A: TRawNodeValueA;
  P: Pointer;
  function _Type: Byte; inline;
  function Count: LongInt; inline;
end;

TNodeValue = record
public
  function _Type: Byte;
  // Истинность проверяется такаже, как и в JsonLogic
  //   int и float -- ненулевые числа
  //   string -- непустая строка
  //   array и list -- непустой массив или список
  //   object -- всегда True
  function IsTrue: Boolean;

  function GetInt(Def: TNodeInt = 0): TNodeInt;
  function GetFloat(Def: TNodeFloat = 0): TNodeFloat;
  function GetStr(Def: PAnsiChar): TNodeString;
  function GetBool(Def: Boolean): Boolean;
  function GetArray(Def: PNodeArray = nil): PNodeArray;
  function GetObject(Def: PNodeObject = nil): PNodeObject;
  function GetList(Def: PNodeItem = nil): PNodeItem;
  function GetUserdata(Def: Pointer = nil): Pointer;
public
case Type_: Byte of
  NODE_INT: (I: TNodeInt);
  NODE_FLOAT: (F: TNodeFloat);
  NODE_STRING: (S: TAnsiView);
  NODE_BOOLEAN: (B: Boolean);
  NODE_ARRAY: (A: PNodeArray);
  NODE_OBJECT: (O: PNodeObject);
  NODE_LIST: (L: PNodeItem);
  NODE_USERDATA: (U: Pointer);
end;

PNodeArrayIterator = ^TNodeArrayIterator;
TNodeArrayIterator = record
private
  FValue: PNodeValue;
  FValueEnd: PNodeValue;
public
  procedure Init(Arr: PNodeArray);
  procedure Done; inline;
  function Next: Boolean;
  property Value: PNodeValue read FValue;
end;

PNodeIterator = ^TNodeIterator;
TNodeIterator = record
private
  FItem: PNodeItem;
  FValue: PNodeValue;
  FValueEnd: PNodeValue;
public
  procedure Init(const Value: TNodeValue);
  procedure Done; inline;
  function Next: Boolean;
  property Value: PNodeValue read FValue;
end;

PNodeValueList = ^TNodeValueList;
TNodeValueList = record
  Value: TNodeValue;
  Next: PNodeValueList;
end;

TNodeArray = record
private
  FCount: LongInt;
  FFirst: PNodeValue;
public
  procedure Init;
  procedure Done;

  function Get(Index: LongInt; Def: PNodeValue): PNodeValue; overload;
  function Get(Index: LongInt; Def: LongInt): LongInt; overload;
  function Get(Index: LongInt; Def: TNodeFloat): TNodeFloat; overload;
  function GetStr(Index: LongInt; Def: TAnsiView): TAnsiView; overload;
  function Get(Index: LongInt; Def: Boolean): Boolean; overload;

  property Count: LongInt read FCount;
  property First: PNodeValue read FFirst;
  property Items: PNodeValue read FFirst;
end;

// NODE_OBJECT and NODE_LIST
TNodeItem = record
public
  Next: PNodeItem;
private
  FHash: TNodeHash;
  FKey: TNodeString;
  FValue: TNodeValue;
  function GetValue: PNodeValue; inline;
public
  procedure Init;
  procedure Done;

  property Hash: TNodeHash read FHash;
  //property Next: PNodeItem read FNext write FNext;
  property Key: TNodeString read FKey write FKey;
  property Value: PNodeValue read GetValue;
end;

{TNodeList = record
private
  FFirst: PNodeItem;
public
  property First: PNodeItem read FFirst write FFirst;
end;}

TNodeObject = record
private
  FBulksCount: LongInt; // number of bulks
  FBulksFirst: PPNodeItem;
  function HashLookup(const Key: TNodeHashedString; out Value: PNodeValue): Boolean;
  function ListLookup(const Key: TNodeString; out Value: PNodeValue): Boolean;
public
  // constructor Init
  procedure Init;

  // destructor Done
  procedure Done;

  function Find(const Key: TNodeHashedString; out Value: PNodeValue): Boolean;
  function Find(const Key: TNodeString; out Value: PNodeValue): Boolean;
  function Get(const Key: TNodeHashedString; Def: TNodeInt): TNodeInt; inline;
  function Get(const Key: TNodeHashedString; Def: TNodeFloat): TNodeFloat; inline;
  function Get(const Key: TNodeHashedString; Def: TNodeString): TNodeString; inline;
  function Get(const Key: TNodeHashedString; Def: Boolean): Boolean; inline;
  function Get(const Key: TNodeHashedString; Def: PNodeObject): PNodeObject; inline;
  function Get(const Key: TNodeHashedString; Def: PNodeArray): PNodeArray; inline;
  function Get(const Key: TNodeHashedString; Def: PNodeItem): PNodeItem; inline;
  function Get(const Key: TNodeHashedString; Def: Pointer): Pointer; inline;
  function GetInt(const Key: TNodeHashedString; Def: TNodeInt = 0): TNodeInt;
  function GetFloat(const Key: TNodeHashedString; Def: TNodeFloat = 0.0): TNodeFloat;
  function GetStr(const Key: TNodeHashedString; Def: PAnsiChar = ''): TNodeString;
  function GetBool(const Key: TNodeHashedString; Def: Boolean = False): Boolean;
  function GetObj(const Key: TNodeHashedString; Def: PNodeObject = nil): PNodeObject;
  function GetArr(const Key: TNodeHashedString; Def: PNodeArray = nil): PNodeArray;
  function GetList(const Key: TNodeHashedString; Def: PNodeItem = nil): PNodeItem;
  function GetUserdata(const Key: TNodeHashedString; Def: Pointer = nil): Pointer;
  function Get(const Key: TNodeString; Def: TNodeInt): TNodeInt; inline;
  function Get(const Key: TNodeString; Def: TNodeFloat): TNodeFloat; inline;
  function Get(const Key: TNodeString; Def: TNodeString): TNodeString; inline;
  function Get(const Key: TNodeString; Def: Boolean): Boolean; inline;
  function Get(const Key: TNodeString; Def: PNodeObject): PNodeObject; inline;
  function Get(const Key: TNodeString; Def: PNodeArray): PNodeArray; inline;
  function Get(const Key: TNodeString; Def: PNodeItem): PNodeItem; inline;
  function Get(const Key: TNodeString; Def: Pointer): Pointer; inline;
  function GetInt(const Key: TNodeString; Def: TNodeInt = 0): TNodeInt;
  function GetFloat(const Key: TNodeString; Def: TNodeFloat = 0.0): TNodeFloat;
  function GetStr(const Key: TNodeString; Def: PAnsiChar = ''): TNodeString;
  function GetBool(const Key: TNodeString; Def: Boolean = False): Boolean;
  function GetObj(const Key: TNodeString; Def: PNodeObject = nil): PNodeObject;
  function GetArr(const Key: TNodeString; Def: PNodeArray = nil): PNodeArray;
  function GetList(const Key: TNodeString; Def: PNodeItem = nil): PNodeItem;
  function GetUserdata(const Key: TNodeString; Def: Pointer = nil): Pointer;

  property BulksCount: LongInt read FBulksCount write FBulksCount;
  property BulksFirst: PPNodeItem read FBulksFirst write FBulksFirst;
end;

// Стратегия этого менеджера:
//   1. Дерево строится один раз, читается много раз
//   2. Удаляется дерево целиком (деструктором TNodeManagerStatic.Done)
//   3. Строки (ключи и NODE_STRING'и) ссылаются на статичные буферы в памяти,
//      поэтому не требуют отдельного освобождения
// Пример использования см. в dnode_json
TNodeManagerStatic = object
public type
  TObjectBuilder = object
  private
    FList: PNodeItem;
  public
    function Start(Manager: PNodeManagerStatic): Boolean;
    function Finish(Manager: PNodeManagerStatic; out Obj: PNodeObject): Boolean;
    function Add(Manager: PNodeManagerStatic; Key: TAnsiView; out Value: PNodeValue): Boolean;
    function AddValue(Manager: PNodeManagerStatic; Key: TAnsiView; const Value: TNodeValue): Boolean;
    function AddNull(Manager: PNodeManagerStatic; Key: TAnsiView): Boolean;
    function AddInt(Manager: PNodeManagerStatic; Key: TAnsiView; Value: LongInt): Boolean;
    function AddFloat(Manager: PNodeManagerStatic; Key: TAnsiView; Value: Double): Boolean;
    function AddBoolean(Manager: PNodeManagerStatic; Key: TAnsiView; Value: Boolean): Boolean;
    function AddString(Manager: PNodeManagerStatic; Key: TAnsiView; Value: TAnsiView): Boolean;
    function AddObject(Manager: PNodeManagerStatic; Key: TAnsiView; Value: PNodeObject): Boolean;
    function AddArray(Manager: PNodeManagerStatic; Key: TAnsiView; Value: PNodeArray): Boolean;
    function AddList(Manager: PNodeManagerStatic; Key: TAnsiView; Value: PNodeItem): Boolean;
  end;
  TArrayBuilder = object
  private
    FList: PNodeItem;
    FCount: LongInt;
  public
    function Start(Manager: PNodeManagerStatic): Boolean;
    function Finish(Manager: PNodeManagerStatic; out Arr: PNodeArray): Boolean;
    function Add(Manager: PNodeManagerStatic; out Value: PNodeValue): Boolean;
    function AddValue(Manager: PNodeManagerStatic; const Value: TNodeValue): Boolean;
    function AddInt(Manager: PNodeManagerStatic; Value: LongInt): Boolean;
    function AddFloat(Manager: PNodeManagerStatic; Value: Double): Boolean;
    function AddBoolean(Manager: PNodeManagerStatic; Value: Boolean): Boolean;
    function AddString(Manager: PNodeManagerStatic; Value: TAnsiView): Boolean;
    function AddObject(Manager: PNodeManagerStatic; Value: PNodeObject): Boolean;
    function AddArray(Manager: PNodeManagerStatic; Value: PNodeArray): Boolean;
    function AddList(Manager: PNodeManagerStatic; Value: PNodeItem): Boolean;
  end;
private
  FChainedAllocator: TChainedAllocator;
  FFreeItem: PNodeItem;
  FStringPool: TStringPool;
  FSourceStart: Pointer;
  FSourceSize: SizeUInt;
  function TryAlloc(Size: SizeUInt; out P: Pointer): Boolean;
public
  // constructor Init:
  //       Initializaes Node Manager
  // Parameters:
  //       MemoryManager: a memory manager for allocation/deallocation
  //                      pass nil for default memory manager from system unit
  procedure Init;

  // destructor Done:
  //       Destroy Node Manager
  procedure Done;

  // Will be FreeMem'ed
  procedure AttachSource(SourceStart: Pointer; SourceSize: SizeUInt);

  function AllocTempItem: PNodeItem;
  procedure DeallocTempItem(Item: PNodeItem);
  procedure DeallocAllTempItems; inline;

  // Выделяет память под объект и сразу же под таблицу с пачками,
  // инициализирует эту таблицу nil'ами
  function AllocObject(BulksCount: LongInt): PNodeObject;
  // Выделяет память под массив и сразу же под его элементы
  // НЕ инициализирует элементы
  function AllocArray(Count: LongInt): PNodeArray;

  //function AllocList: PNodeItem;
  //function NewBuffer(Count: LongInt): Pointer;

  function AllocItem(var First: PNodeItem): Boolean;
  function AllocItemInList(var First: PNodeItem;
                           var Last: PNodeItem): Boolean;
end;

// Default hash
function NodeHash(const Key: TNodeString): TNodeHash; inline;
function NodeMakeHashedString(const Key: TNodeString): TNodeHashedString; inline;
operator := (const Key: TNodeString): TNodeHashedString; inline;

procedure NodeSetNull(out Value: TNodeValue); inline;
procedure NodeSetInt(out Value: TNodeValue; I: TNodeInt); inline;
procedure NodeSetFloat(out Value: TNodeValue; F: TNodeFloat); inline;
procedure NodeSetString(out Value: TNodeValue; S: TNodeString); inline;
procedure NodeSetBoolean(out Value: TNodeValue; B: Boolean); inline;
procedure NodeSetArray(out Value: TNodeValue; A: PNodeArray); inline;
procedure NodeSetObject(out Value: TNodeValue; O: PNodeObject); inline;
procedure NodeSetList(out Value: TNodeValue; L: PNodeItem); inline;

function NodeEmptyObject: PNodeObject; inline;
function NodeEmptyArray: PNodeArray; inline;
function NodeEmptyList: PNodeItem; inline;

// У всех ключей должны быть подсчитаны хеши, если число пачек больше 1
// Память под пачки должна быть уже выделена и проинициализирована
// Эта функция "перевернёт список"
procedure NodePutItemsToObject(Item: PNodeItem; var Obj: TNodeObject);

// (Неглубоко) копирует элементы из списка в массив
// Никаких проверок границ не производится
procedure NodeCopyItemsToArray(Item: PNodeItem; var Arr: TNodeArray);

var
  // This values are initialized in initialization section of this unit
  // Never change it in client code
  NODE_EMPTY_ARRAY: Pointer;
  NODE_EMPTY_OBJECT: Pointer;
  NODE_EMPTY_LIST: Pointer;

// Temporary utils for dfps_script
function AddFloatToList(M: PNodeManagerStatic; L: PPNodeItem; F: TNodeFloat): Boolean;

implementation

function TryResizeArray(var First: Pointer;
                        ElementSize: LongInt;
                        NewCount: LongInt): Boolean; inline;
begin
  if NewCount = 0 then begin
    FreeMem(First);
    First := nil;
    Result := True;
  end else begin
    First := ReAllocMem(First, NewCount * ElementSize);
    Result := First <> nil;
  end;
end;

function NodeHash(const Key: TNodeString): TNodeHash;
begin
  Result := HashPjw32(Key.Data, Key.Length);
end;

function NodeMakeHashedString(const Key: TNodeString): TNodeHashedString;
begin
  Result.S := Key;
  Result.H := NodeHash(Key);
end;

operator := (const Key: TNodeString): TNodeHashedString; inline;
begin
  Result.S := Key;
  Result.H := NodeHash(Key);
end;

procedure NodeSetNull(out Value: TNodeValue);
begin
  Value.Type_ := NODE_NULL;
end;

procedure NodeSetInt(out Value: TNodeValue; I: TNodeInt);
begin
  Value.Type_ := NODE_INT;
  Value.I := I;
end;

procedure NodeSetFloat(out Value: TNodeValue; F: TNodeFloat);
begin
  Value.Type_ := NODE_FLOAT;
  Value.F := F;
end;

procedure NodeSetString(out Value: TNodeValue; S: TNodeString);
begin
  Value.Type_ := NODE_STRING;
  Value.S := S;
end;

procedure NodeSetBoolean(out Value: TNodeValue; B: Boolean);
begin
  Value.Type_ := NODE_BOOLEAN;
  Value.B := B;
end;

procedure NodeSetArray(out Value: TNodeValue; A: PNodeArray);
begin
  Value.Type_ := NODE_ARRAY;
  Value.A := A;
end;

procedure NodeSetObject(out Value: TNodeValue; O: PNodeObject);
begin
  Value.Type_ := NODE_OBJECT;
  Value.O := O;
end;

procedure NodeSetList(out Value: TNodeValue; L: PNodeItem); inline;
begin
  Value.Type_ := NODE_LIST;
  Value.L := L;
end;

function TRawNodeValue._Type: Byte;
begin
  Result := A._Type;
end;

function TRawNodeValue.Count: LongInt;
begin
  Result := A.Count;
end;

function TNodeValue._Type: Byte;
begin
  Result := Type_;
end;

function TNodeValue.IsTrue: Boolean;
begin
  case Type_ of
    NODE_NULL: Result := False;
    NODE_INT: Result := I <> 0;
    NODE_FLOAT: Result := F <> 0;
    NODE_STRING: Result := S.Length <> 0;
    NODE_BOOLEAN: Result := B;
    NODE_ARRAY: Result := A^.Count > 0;
    NODE_OBJECT: Result := True;
    NODE_LIST: Result := L <> nil;
  else
    Result := False;
  end;
end;

function TNodeValue.GetInt(Def: TNodeInt = 0): TNodeInt;
begin
  if Type_ = NODE_INT then begin
    Result := I;
  end else
    Result := Def;
end;

function TNodeValue.GetFloat(Def: TNodeFloat = 0): TNodeFloat;
begin
  if Type_ = NODE_FLOAT then begin
    Result := F;
  end else
    Result := Def;
end;

function TNodeValue.GetStr(Def: PAnsiChar): TNodeString;
begin
  if Type_ = NODE_STRING then begin
    Result := S;
  end else
    Result := Def;
end;

function TNodeValue.GetBool(Def: Boolean): Boolean;
begin
  if Type_ = NODE_BOOLEAN then begin
    Result := B;
  end else
    Result := Def;
end;

function TNodeValue.GetArray(Def: PNodeArray = nil): PNodeArray;
begin
  if Type_ = NODE_ARRAY then begin
    Result := A;
  end else
    Result := Def;
end;

function TNodeValue.GetObject(Def: PNodeObject = nil): PNodeObject;
begin
  if Type_ = NODE_OBJECT then begin
    Result := O;
  end else
    Result := Def;
end;

function TNodeValue.GetList(Def: PNodeItem = nil): PNodeItem;
begin
  if Type_ = NODE_LIST then begin
    Result := L;
  end else
    Result := Def;
end;

function TNodeValue.GetUserdata(Def: Pointer = nil): Pointer;
begin
  if Type_ = NODE_USERDATA then begin
    Result := U;
  end else
    Result := Def;
end;

procedure TNodeArrayIterator.Init(Arr: PNodeArray);
begin
  FValue := Arr^.FFirst;
  if FValue = nil then begin
    FValueEnd := nil;
  end else begin
    Dec(FValue);
    FValueEnd := Arr^.FFirst + Arr^.FCount;
  end;
end;

procedure TNodeArrayIterator.Done;
begin
end;

function TNodeArrayIterator.Next: Boolean;
begin
  Inc(FValue);
  Result := FValue < FValueEnd;
end;

procedure TNodeIterator.Init(const Value: TNodeValue);
begin
  if Value.Type_ = NODE_ARRAY then begin
    FItem := nil;
    FValue := Value.A^.First;
    if FValue <> nil then
      Dec(FValue);
    FValueEnd := Value.A^.First + Value.A^.Count;
  end else if Value.Type_ = NODE_LIST then begin
    FItem := Value.L;
    FValueEnd := nil;
  end else begin
    FItem := nil;
    FValue := nil;
    FValueEnd := nil;
  end;
end;

procedure TNodeIterator.Done;
begin
end;

function TNodeIterator.Next: Boolean;
begin
  if FItem <> nil then begin
    FValue := FItem^.Value;
    FItem := FItem^.Next;
    Exit(True);
  end else begin
    Inc(FValue);
    Result := (FValueEnd <> nil) and (FValue < FValueEnd);
  end;
end;

procedure TNodeArray.Init;
begin
  FCount := 0;
  FFirst := nil;
end;

procedure TNodeArray.Done;
begin
end;

function TNodeArray.Get(Index: LongInt; Def: PNodeValue): PNodeValue;
begin
  if (0 <= Index) and (Index < FCount) then begin
    Result := @FFirst[Index];
  end else
    Result := Def;
end;

function TNodeArray.Get(Index: LongInt; Def: LongInt): LongInt;
begin
  if (0 <= Index) and (Index < FCount) and (FFirst[Index].Type_ = NODE_STRING) then begin
    Result := FFirst[Index].I;
  end else
    Result := Def;
end;

function TNodeArray.Get(Index: LongInt; Def: TNodeFloat): TNodeFloat;
begin
  if (0 <= Index) and (Index < FCount) and (FFirst[Index].Type_ = NODE_FLOAT) then begin
    Result := FFirst[Index].F;
  end else
    Result := Def;
end;

function TNodeArray.GetStr(Index: LongInt; Def: TAnsiView): TAnsiView;
begin
  if (0 <= Index) and (Index < FCount) and (FFirst[Index].Type_ = NODE_STRING) then begin
    Result := FFirst[Index].S;
  end else
    Result := Def;
end;

function TNodeArray.Get(Index: LongInt; Def: Boolean): Boolean;
begin
  if (0 <= Index) and (Index < FCount) and (FFirst[Index].Type_ = NODE_BOOLEAN) then begin
    Result := FFirst[Index].B;
  end else
    Result := Def;
end;

function TNodeItem.GetValue: PNodeValue;
begin
  Result := @FValue;
end;

procedure TNodeItem.Init;
begin
end;

procedure TNodeItem.Done;
begin
end;

function TNodeObject.HashLookup(const Key: TNodeHashedString; out Value: PNodeValue): Boolean;
var
  L: PNodeItem;
begin
  L := FBulksFirst[Key.H mod FBulksCount];
  while L <> nil do begin
    if L^.FHash = Key.H then begin
      if Key.S = L^.FKey then begin
        Value := L^.Value;
        Exit(True);
      end;
    end;
    L := L^.Next;
  end;
  Exit(False);
end;

function TNodeObject.ListLookup(const Key: TNodeString; out Value: PNodeValue): Boolean;
var
  L: PNodeItem;
begin
  L := FBulksFirst[0];
  while L <> nil do begin
    if Key = L^.FKey then begin
      Value := L^.Value;
      Exit(True);
    end;
    L := L^.Next;
  end;
  Exit(False);
end;

procedure TNodeObject.Init;
begin
end;

procedure TNodeObject.Done;
begin
end;

function TNodeObject.Find(const Key: TNodeHashedString; out Value: PNodeValue): Boolean;
begin
  if FBulksCount > 1 then begin
    Result := HashLookup(Key, Value);
  end else begin
    Result := ListLookup(Key.S, Value);
  end;
end;

function TNodeObject.Find(const Key: TNodeString; out Value: PNodeValue): Boolean;
begin
  if FBulksCount > 1 then begin
    Result := HashLookup(TNodeHashedString(Key), Value);
  end else begin
    Result := ListLookup(Key, Value);
  end;
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: TNodeInt): TNodeInt;
begin
  Result := GetInt(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: TNodeFloat): TNodeFloat;
begin
  Result := GetFloat(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: TNodeString): TNodeString;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_STRING) then begin
    Result := V^.S;
  end else
    Result := Def;
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: Boolean): Boolean;
begin
  Result := GetBool(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: PNodeObject): PNodeObject;
begin
  Result := GetObj(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: PNodeArray): PNodeArray;
begin
  Result := GetArr(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: PNodeItem): PNodeItem;
begin
  Result := GetList(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeHashedString; Def: Pointer): Pointer;
begin
  Result := GetUserdata(Key, Def);
end;

function TNodeObject.GetInt(const Key: TNodeHashedString; Def: TNodeInt = 0): TNodeInt;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_INT) then begin
    Result := V^.I;
  end else
    Result := Def;
end;

function TNodeObject.GetFloat(const Key: TNodeHashedString; Def: TNodeFloat = 0.0): TNodeFloat;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_FLOAT) then begin
    Result := V^.F;
  end else
    Result := Def;
end;

function TNodeObject.GetStr(const Key: TNodeHashedString; Def: PAnsiChar = ''): TNodeString;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_STRING) then begin
    Result := V^.S;
  end else
    Result := Def;
end;

function TNodeObject.GetBool(const Key: TNodeHashedString; Def: Boolean = False): Boolean;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_BOOLEAN) then begin
    Result := V^.B;
  end else
    Result := Def;
end;

function TNodeObject.GetObj(const Key: TNodeHashedString; Def: PNodeObject = nil): PNodeObject;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_OBJECT) then begin
    Result := V^.O;
  end else
    Result := Def;
end;

function TNodeObject.GetArr(const Key: TNodeHashedString; Def: PNodeArray = nil): PNodeArray;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_ARRAY) then begin
    Result := V^.A;
  end else
    Result := Def;
end;

function TNodeObject.GetList(const Key: TNodeHashedString; Def: PNodeItem = nil): PNodeItem;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_LIST) then begin
    Result := V^.L;
  end else
    Result := Def;
end;

function TNodeObject.GetUserdata(const Key: TNodeHashedString; Def: Pointer = nil): Pointer;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_USERDATA) then begin
    Result := V^.U;
  end else
    Result := Def;
end;

function TNodeObject.Get(const Key: TNodeString; Def: TNodeInt): TNodeInt;
begin
  Result := GetInt(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeString; Def: TNodeFloat): TNodeFloat;
begin
  Result := GetFloat(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeString; Def: TNodeString): TNodeString;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_STRING) then begin
    Result := V^.S;
  end else
    Result := Def;
end;

function TNodeObject.Get(const Key: TNodeString; Def: Boolean): Boolean;
begin
  Result := GetBool(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeString; Def: PNodeObject): PNodeObject;
begin
  Result := GetObj(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeString; Def: PNodeArray): PNodeArray;
begin
  Result := GetArr(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeString; Def: PNodeItem): PNodeItem;
begin
  Result := GetList(Key, Def);
end;

function TNodeObject.Get(const Key: TNodeString; Def: Pointer): Pointer;
begin
  Result := GetUserdata(Key, Def);
end;

function TNodeObject.GetInt(const Key: TNodeString; Def: TNodeInt = 0): TNodeInt;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_INT) then begin
    Result := V^.I;
  end else
    Result := Def;
end;

function TNodeObject.GetFloat(const Key: TNodeString; Def: TNodeFloat = 0.0): TNodeFloat;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_FLOAT) then begin
    Result := V^.F;
  end else
    Result := Def;
end;

function TNodeObject.GetStr(const Key: TNodeString; Def: PAnsiChar = ''): TNodeString;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_STRING) then begin
    Result := V^.S;
  end else
    Result := Def;
end;

function TNodeObject.GetBool(const Key: TNodeString; Def: Boolean = False): Boolean;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_BOOLEAN) then begin
    Result := V^.B;
  end else
    Result := Def;
end;

function TNodeObject.GetObj(const Key: TNodeString; Def: PNodeObject = nil): PNodeObject;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_OBJECT) then begin
    Result := V^.O;
  end else
    Result := Def;
end;

function TNodeObject.GetArr(const Key: TNodeString; Def: PNodeArray = nil): PNodeArray;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_ARRAY) then begin
    Result := V^.A;
  end else
    Result := Def;
end;

function TNodeObject.GetList(const Key: TNodeString; Def: PNodeItem = nil): PNodeItem;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_LIST) then begin
    Result := V^.L;
  end else
    Result := Def;
end;

function TNodeObject.GetUserdata(const Key: TNodeString; Def: Pointer = nil): Pointer;
var
  V: PNodeValue;
begin
  if Find(Key, V) and (V^.Type_ = NODE_USERDATA) then begin
    Result := V^.U;
  end else
    Result := Def;
end;

function TNodeManagerStatic.TObjectBuilder.Start(Manager: PNodeManagerStatic): Boolean;
begin
  FList := nil;
  Result := True;
end;

function TNodeManagerStatic.TObjectBuilder.Finish(Manager: PNodeManagerStatic; out Obj: PNodeObject): Boolean;
begin
  Obj := Manager^.AllocObject(1);
  if Obj = nil then
    Exit(False);
  Obj^.BulksFirst[0] := FList;
  FList := nil;
  Exit(True);
end;

function TNodeManagerStatic.TObjectBuilder.Add(Manager: PNodeManagerStatic; Key: TAnsiView; out Value: PNodeValue): Boolean;
begin
  Result := Manager^.AllocItem(FList);
  if Result then begin
    FList^.Key := Key;
    Value := FList^.Value;
  end;
end;

function TNodeManagerStatic.TObjectBuilder.AddValue(Manager: PNodeManagerStatic; Key: TAnsiView; const Value: TNodeValue): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    V^ := Value;
end;

function TNodeManagerStatic.TObjectBuilder.AddNull(Manager: PNodeManagerStatic; Key: TAnsiView): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetNull(V^);
end;

function TNodeManagerStatic.TObjectBuilder.AddInt(Manager: PNodeManagerStatic; Key: TAnsiView; Value: LongInt): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetInt(V^, Value);
end;

function TNodeManagerStatic.TObjectBuilder.AddFloat(Manager: PNodeManagerStatic; Key: TAnsiView; Value: Double): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetFloat(V^, Value);
end;

function TNodeManagerStatic.TObjectBuilder.AddBoolean(Manager: PNodeManagerStatic; Key: TAnsiView; Value: Boolean): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetBoolean(V^, Value);
end;

function TNodeManagerStatic.TObjectBuilder.AddString(Manager: PNodeManagerStatic; Key: TAnsiView; Value: TAnsiView): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetString(V^, Value);
end;

function TNodeManagerStatic.TObjectBuilder.AddObject(Manager: PNodeManagerStatic; Key: TAnsiView; Value: PNodeObject): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetObject(V^, Value);
end;

function TNodeManagerStatic.TObjectBuilder.AddArray(Manager: PNodeManagerStatic; Key: TAnsiView; Value: PNodeArray): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetArray(V^, Value);
end;

function TNodeManagerStatic.TObjectBuilder.AddList(Manager: PNodeManagerStatic; Key: TAnsiView; Value: PNodeItem): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, Key, V);
  if Result then
    NodeSetList(V^, Value);
end;

function TNodeManagerStatic.TArrayBuilder.Start(Manager: PNodeManagerStatic): Boolean;
begin
  FList := nil;
  FCount := 0;
  Result := True;
end;

function TNodeManagerStatic.TArrayBuilder.Finish(Manager: PNodeManagerStatic; out Arr: PNodeArray): Boolean;
var
  It: PNodeValue;
begin
  Arr := Manager^.AllocArray(FCount);
  if Arr <> nil then begin
    It := Arr^.First + Arr^.Count - 1;
    while FList <> nil do begin
      It^ := FList^.Value^;
      Dec(It);
      FList := Flist^.Next;
    end;
    Exit(True);
  end else
    Exit(False);
end;

function TNodeManagerStatic.TArrayBuilder.Add(Manager: PNodeManagerStatic; out Value: PNodeValue): Boolean;
begin
  Result := Manager^.AllocItem(FList);
  if Result then begin
    Value := FList^.Value;
    Inc(FCount);
  end;
end;

function TNodeManagerStatic.TArrayBuilder.AddValue(Manager: PNodeManagerStatic; const Value: TNodeValue): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    V^ := Value;
end;

function TNodeManagerStatic.TArrayBuilder.AddInt(Manager: PNodeManagerStatic; Value: LongInt): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    NodeSetInt(V^, Value);
end;

function TNodeManagerStatic.TArrayBuilder.AddFloat(Manager: PNodeManagerStatic; Value: Double): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    NodeSetFloat(V^, Value);
end;

function TNodeManagerStatic.TArrayBuilder.AddBoolean(Manager: PNodeManagerStatic; Value: Boolean): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    NodeSetBoolean(V^, Value);
end;

function TNodeManagerStatic.TArrayBuilder.AddString(Manager: PNodeManagerStatic; Value: TAnsiView): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    NodeSetString(V^, Value);
end;

function TNodeManagerStatic.TArrayBuilder.AddObject(Manager: PNodeManagerStatic; Value: PNodeObject): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    NodeSetObject(V^, Value);
end;

function TNodeManagerStatic.TArrayBuilder.AddArray(Manager: PNodeManagerStatic; Value: PNodeArray): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    NodeSetArray(V^, Value);
end;

function TNodeManagerStatic.TArrayBuilder.AddList(Manager: PNodeManagerStatic; Value: PNodeItem): Boolean;
var
  V: PNodeValue;
begin
  Result := Add(Manager, V);
  if Result then
    NodeSetList(V^, Value);
end;

function TNodeManagerStatic.TryAlloc(Size: SizeUInt; out P: Pointer): Boolean;
const
  PAGE = 64 shl 10;
begin
  Result := FChainedAllocator.TryAllocLeft(Size, P);
  if not Result then begin
    P := TryGetMem(PAGE);
    Result := P <> nil;
    if Result then begin
      FChainedAllocator.AddHeapMemory(P, PAGE);
    end;
    Result := FChainedAllocator.TryAllocLeft(Size, P);
  end;
end;

procedure TNodeManagerStatic.Init;
begin
  FFreeItem := nil;
  FStringPool.Init;
  FSourceStart := nil;
  FSourceSize := 0;
  FChainedAllocator.Init;
end;

procedure TNodeManagerStatic.Done;
begin
  FStringPool.Done;
  if FSourceStart <> nil then begin
    FreeMem(FSourceStart);
  end;
  FChainedAllocator.Done;
end;

procedure TNodeManagerStatic.AttachSource(SourceStart: Pointer; SourceSize: SizeUInt);
begin
  FSourceStart := SourceStart;
  FSourceSize := SourceSize;
end;

function TNodeManagerStatic.AllocTempItem: PNodeItem;
begin
  if FFreeItem <> nil then begin
    Result := FFreeItem;
    FFreeItem := FFreeItem^.Next;
  end else begin
    Result := FChainedAllocator.TryAllocRight(SizeOf(TNodeItem));
  end;
end;

procedure TNodeManagerStatic.DeallocTempItem(Item: PNodeItem);
begin
  Item^.Next := FFreeItem;
  FFreeItem := Item;
end;

procedure TNodeManagerStatic.DeallocAllTempItems;
begin
  FChainedAllocator.DeallocAllRight;
  FFreeItem := nil;
end;

function TNodeManagerStatic.AllocObject(BulksCount: LongInt): PNodeObject;
var
  I: LongInt;
begin
  if TryAlloc(SizeOf(TNodeObject) + SizeOf(PNodeItem) * BulksCount, Pointer(Result)) then begin
    Result^.FBulksCount := BulksCount;
    Result^.FBulksFirst := PPNodeItem(Result + 1);
    for I := 0 to BulksCount - 1 do
      Result^.FBulksFirst[I] := nil;
  end else
    Result := nil;
end;

function TNodeManagerStatic.AllocArray(Count: LongInt): PNodeArray;
begin
  if TryAlloc(SizeOf(TNodeArray) + SizeOf(TNodeValue) * Count, Pointer(Result)) then begin
    Result^.FCount := Count;
    Result^.FFirst := PNodeValue(Result + 1);
  end else
    Result := nil;
end;

function TNodeManagerStatic.AllocItem(var First: PNodeItem): Boolean;
var
  Temp: PNodeItem;
begin
  Result := TryAlloc(SizeOf(TNodeItem), Pointer(Temp));
  if Result then begin
    Temp^.Next := First;
    Temp^.Key := '';
    First := Temp;
  end;
end;

function TNodeManagerStatic.AllocItemInList(var First: PNodeItem;
                         var Last: PNodeItem): Boolean;
var
  Temp: Pointer;
begin
  Result := TryAlloc(SizeOf(TNodeItem), Temp);
  if Result then begin
    if Last = nil then begin
      First := Temp;
      Last := First;
    end else begin
      Last^.Next := Temp;
      Last := Last^.Next;
    end;
    Last^.Next := nil;
  end;
end;

//function TNodeManagerStatic.AddString(S: TAnsiView; out N: TNodeString): Boolean;
//var
//  Res: TAnsiView;
//begin
//  Inc(S.S);
//  Result := FStringPool.Append(S, Res);
//  PAnsiChar(S.P)[S.S - 1] := #0;
//  N := Res.P;
//end;

var
  EmptyList: PNodeItem = nil;
  EmptyObject: TNodeObject;
function NodeEmptyObject: PNodeObject;
begin
  Result := @EmptyObject;
end;

var
  EmptyArray: TNodeArray;
function NodeEmptyArray: PNodeArray;
begin
  Result := @EmptyArray;
end;

function NodeEmptyList: PNodeItem;
begin
  Result := nil;
end;

procedure NodePutItemsToObject(Item: PNodeItem; var Obj: TNodeObject);
var
  Next: PNodeItem;
  Bulk: PPNodeItem;
begin
  while Item <> nil do begin
    Next := Item^.Next;
    Bulk := @Obj.FBulksFirst[Item^.FHash mod Obj.FBulksCount];
    Item^.Next := Bulk^;
    Bulk^ := Item;
    Item := Next;
  end;
end;

procedure NodeCopyItemsToArray(Item: PNodeItem; var Arr: TNodeArray);
var
  I: PNodeValue;
begin
  I := Arr.FFirst;
  while Item <> nil do begin
    I^ := Item^.Value^;
    Item := Item^.Next;
    Inc(I);
  end;
end;

function AddFloatToList(M: PNodeManagerStatic; L: PPNodeItem; F: TNodeFloat): Boolean;
begin
  if not M^.AllocItem(L^) then
    Exit(False);
  (L^)^.Key := '';
  NodeSetFloat((L^)^.Value^, F);
  Exit(True);
end;

initialization
  EmptyList := nil;
  EmptyObject.FBulksCount := 1;
  EmptyObject.FBulksFirst := @EmptyList;
  EmptyArray.FCount := 0;
  EmptyArray.FFirst := nil;
  NODE_EMPTY_ARRAY := NodeEmptyArray;
  NODE_EMPTY_OBJECT := NodeEmptyObject;
  NODE_EMPTY_LIST := NodeEmptyList;
end.
