ulid_20240205.0.0_455f3c7d/ulid.adb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
with Ada.Calendar.Formatting;

package body ULID is

  function Encode (Code : ULID_Number) return String is
    --  Crockford's Base32 (5 bits per symbol):
    crockford : constant String := "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
    result : String (1 .. 26);
    --  ^ The first 10 characters contain a time information (50 bits,
    --    of them 48 are used); the 16 other characters represent a random
    --    number (80 bits).
    x : ULID_Number := Code;
  begin
    for c of reverse result loop
      c := crockford (1 + Integer (x and 31));
      x := x / 32;
    end loop;
    return result;
  end Encode;

  procedure Reset (Generator : Random_Generator) is
  begin
    Random_Numbers.Reset (Random_Numbers.Generator (Generator));
  end Reset;

  procedure Reset (Generator : Random_Generator; Seed : Integer) is
  begin
    Random_Numbers.Reset (Random_Numbers.Generator (Generator), Seed);
  end Reset;

  function Generate_Time_Part
    (Leap_Second : Boolean;
     Offset      : Ada.Calendar.Time_Zones.Time_Offset)
  return ULID_Number
  is
    use Ada.Calendar;
    unix_epoch : constant Time :=
      Ada.Calendar.Formatting.Time_Of
        (1970, 1, 1, 0.0, Leap_Second, Offset);
    milliseconds_clock : constant ULID_Number :=
      ULID_Number (1000.0 * (Clock - unix_epoch));
  begin
    return milliseconds_clock * 2 ** 80;
  end Generate_Time_Part;

  function
    Generate_Random_Part (Generator : Random_Generator) return ULID_Number
  is
  begin
    return ULID_Number
      (Random_Numbers.Random
        (Random_Numbers.Generator (Generator)));
  end Generate_Random_Part;

  function Generate
    (Generator   : Random_Generator;
     Leap_Second : Boolean := False;
     Offset      : Ada.Calendar.Time_Zones.Time_Offset := 0)
  return ULID_Number
  is
  (Generate_Time_Part (Leap_Second, Offset) +
   Generate_Random_Part (Generator));

  function Generate_Monotonic
    (Previous    : ULID_Number;
     Generator   : Random_Generator;
     Leap_Second : Boolean := False;
     Offset      : Ada.Calendar.Time_Zones.Time_Offset := 0)
  return ULID_Number
  is
    mask : constant :=  (2 ** 128 - 1) - (2 ** 80 - 1);
    old_time_part : constant ULID_Number := Previous and mask;
    new_time_part : constant ULID_Number :=
      Generate_Time_Part (Leap_Second, Offset);
  begin
    if new_time_part <= old_time_part then
      return Previous + 1;
    else
      return new_time_part + Generate_Random_Part (Generator);
    end if;
  end Generate_Monotonic;

  function Network_Byte_Order (Code : ULID_Number) return Byte_Array is
    result : Byte_Array;
    rest : ULID_Number := Code;
  begin
    for i in reverse result'Range loop
      result (i) := Byte (rest and 255);
      rest := rest / 256;
    end loop;
    return result;
  end Network_Byte_Order;

end ULID;