--
--  Copyright (C) 2019-2023, AdaCore
--
--  SPDX-License-Identifier: Apache-2.0 WITH LLVM-Exception
--

--  This is an internal GPR2 Unit used to implement a cache for attribute
--  evaluation. The cache protocol is the following:
--
--  C := Check_Cache (...);
--  if Has_Element (C) then
--     return Element (C);
--  end if;
--
--  Schedule_Update_Cache;
--
--  ... compute value to cache ...
--
--  Update_Cache (..., New_Value);
--
--  The cache is thread safe and lock-free on read operations. The lock-free
--  read is possible by using using some extra memory. It relies also in the
--  fact that in Indefinite_Hashed_Maps reallocation is done only if there is
--  need for new capacity.

private with Ada.Finalization;

with GPR2.Project.Attribute;
with GPR2.Project.Attribute_Index;

package GPR2.Project.Attribute_Cache is

   type Object is tagged private;

   type Cursor is private;

   function Check_Cache
     (Self   : Object;
      Name   : Q_Attribute_Id;
      Index  : Project.Attribute_Index.Object := Attribute_Index.Undefined;
      At_Pos : Unit_Index                     := No_Index)
      return Cursor;

   function Has_Element (C : Cursor) return Boolean;

   function Element (C : Cursor) return GPR2.Project.Attribute.Object;

   procedure Schedule_Update_Cache (Self : Object);

   procedure Update_Cache
     (Self   : Object;
      Name   : Q_Attribute_Id;
      Index  : Project.Attribute_Index.Object := Attribute_Index.Undefined;
      At_Pos : Unit_Index                     := No_Index;
      Attr   : GPR2.Project.Attribute.Object);

   procedure Clear_Cache (Self : Object);
   --  Empty the cache

   procedure Disable_Cache (Self : Object);
   --  Empty the cache, and do not store anything until the cache is re-enabled

   procedure Enable_Cache (Self : Object);
   --  Re-enable storage in the cache

private

   package Attribute_Cache_Maps is
        new Ada.Containers.Indefinite_Hashed_Maps
           (Key_Type        => String,
            Element_Type    => Project.Attribute.Object,
            Hash            => Ada.Strings.Hash,
            Equivalent_Keys => "=",
            "="             => Project.Attribute."=");

   type Map_Access is access Attribute_Cache_Maps.Map;

   type Count_Type_Access is access Ada.Containers.Count_Type;

   type Cursor is new Attribute_Cache_Maps.Cursor;

   type Inner_Object is record
      Enabled      : Boolean := True;
      Table        : Map_Access := null;
      Former_Table : Map_Access := null;
      Needed_Extra_Capacity : Ada.Containers.Count_Type := 0;
   end record;

   type Inner_Object_Access is access Inner_Object;

   type Object is new Ada.Finalization.Controlled with record
      Inner : Inner_Object_Access := null;
   end record;

   overriding procedure Initialize (Cache : in out Object);
   overriding procedure Finalize (Cache : in out Object);
   overriding procedure Adjust (Cache : in out Object);

   Min_Cache_Size : constant Ada.Containers.Count_Type := 128;
end GPR2.Project.Attribute_Cache;
