From 39dde41516b09182391a4a8afbe6819b2eff419f Mon Sep 17 00:00:00 2001
From: BONNAURE Olivier <o.bonnaure@solisoft.net>
Date: Fri, 12 Apr 2024 17:10:27 +0200
Subject: [PATCH] [Redbean] Add UuidV4 method  (#1140)

---
 test/tool/net/uuidv4_test.lua | 25 +++++++++++++++++++++++++
 tool/net/definitions.lua      |  4 ++++
 tool/net/help.txt             |  3 +++
 tool/net/lfuncs.c             | 25 +++++++++++++++++++++++++
 tool/net/lfuncs.h             |  1 +
 tool/net/redbean.c            |  1 +
 6 files changed, 59 insertions(+)
 create mode 100644 test/tool/net/uuidv4_test.lua

diff --git a/test/tool/net/uuidv4_test.lua b/test/tool/net/uuidv4_test.lua
new file mode 100644
index 000000000..08b4ac3c7
--- /dev/null
+++ b/test/tool/net/uuidv4_test.lua
@@ -0,0 +1,25 @@
+-- Copyright 2024 Justine Alexandra Roberts Tunney
+--
+-- Permission to use, copy, modify, and/or distribute this software for
+-- any purpose with or without fee is hereby granted, provided that the
+-- above copyright notice and this permission notice appear in all copies.
+--
+-- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+-- WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+-- WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+-- AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+-- DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+-- PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+-- TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+-- PERFORMANCE OF THIS SOFTWARE.
+for i = 1, 1000000 do
+  local uuid = UuidV4()
+  assert(#uuid == 36)
+  assert(string.sub(uuid, 9, 9) == "-")
+  assert(string.sub(uuid, 14, 14) == "-")
+  assert(string.sub(uuid, 15, 15) == "4")
+  assert(string.sub(uuid, 19, 19) == "-")
+  y = string.sub(uuid, 20, 20)
+  assert(y == "8" or y == "9" or y == "a" or y == "b")
+  assert(string.sub(uuid, 24, 24) == "-")
+end
diff --git a/tool/net/definitions.lua b/tool/net/definitions.lua
index 988e3ba6f..f6fecffda 100644
--- a/tool/net/definitions.lua
+++ b/tool/net/definitions.lua
@@ -1977,6 +1977,10 @@ function VisualizeControlCodes(str) end
 ---@nodiscard
 function Underlong(str) end
 
+--- Generate a uuid_v4
+--- @return string
+function UuidV4() end
+
 ---@param x integer
 ---@return integer # position of first bit set.
 --- Passing `0` will raise an error. Same as the Intel x86 instruction BSF.
diff --git a/tool/net/help.txt b/tool/net/help.txt
index 8095149cd..ab6af435d 100644
--- a/tool/net/help.txt
+++ b/tool/net/help.txt
@@ -1062,6 +1062,9 @@ FUNCTIONS
           Server Name Indicator (SNI) when performing Fetch() requests.
           This function is not available in unsecure mode.
 
+  UuidV4() -> str
+          Returns an uuid v4 string.
+
   Fetch(url:str[,body:str|{method=value:str,body=value:str,headers=table,...}])
       ├─→ status:int, {header:str=value:str,...}, body:str
       └─→ nil, error:str
diff --git a/tool/net/lfuncs.c b/tool/net/lfuncs.c
index 1949f1649..79f0d1ca0 100644
--- a/tool/net/lfuncs.c
+++ b/tool/net/lfuncs.c
@@ -829,6 +829,31 @@ int LuaVisualizeControlCodes(lua_State *L) {
   return LuaCoder(L, VisualizeControlCodes);
 }
 
+int LuaUuidV4(lua_State *L) {
+  static const char v[] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                           '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+  char uuid_str[37] = {0};
+  uint64_t r = _rand64();
+  int j = 0;
+  for (int i = 0; i < 36; ++i, ++j) {
+    if (j == 16) {
+      r = _rand64();
+      j = 0;
+    }
+    uuid_str[i] = v[(r & (0xfull << (j * 4ull))) >> (j * 4ull)];
+  }
+
+  uuid_str[8] = '-';
+  uuid_str[13] = '-';
+  uuid_str[14] = '4';
+  uuid_str[18] = '-';
+  uuid_str[19] = v[8 | (r & (0x3ull << (j * 4ull))) >> (j * 4ull)];
+  uuid_str[23] = '-';
+  uuid_str[36] = '\0';
+  lua_pushfstring(L, uuid_str);
+  return 1;
+}
+
 static dontinline int LuaHasherImpl(lua_State *L, size_t k,
                                     int H(const void *, size_t, uint8_t *)) {
   size_t n;
diff --git a/tool/net/lfuncs.h b/tool/net/lfuncs.h
index c5fcf8796..ab82c4e95 100644
--- a/tool/net/lfuncs.h
+++ b/tool/net/lfuncs.h
@@ -90,6 +90,7 @@ int LuaSleep(lua_State *);
 int LuaSlurp(lua_State *);
 int LuaUncompress(lua_State *);
 int LuaUnderlong(lua_State *);
+int LuaUuidV4(lua_State *);
 int LuaVisualizeControlCodes(lua_State *);
 
 void LuaPushUrlView(lua_State *, struct UrlView *);
diff --git a/tool/net/redbean.c b/tool/net/redbean.c
index e3c741b7a..144e0dcbd 100644
--- a/tool/net/redbean.c
+++ b/tool/net/redbean.c
@@ -5287,6 +5287,7 @@ static const luaL_Reg kLuaFuncs[] = {
     {"StoreAsset", LuaStoreAsset},                              //
     {"Uncompress", LuaUncompress},                              //
     {"Underlong", LuaUnderlong},                                //
+    {"UuidV4", LuaUuidV4},                                      //
     {"VisualizeControlCodes", LuaVisualizeControlCodes},        //
     {"Write", LuaWrite},                                        //
     {"bin", LuaBin},                                            //