mirror of
https://github.com/penpot/penpot.git
synced 2025-01-04 13:50:12 -05:00
🐛 Fix incorrect encoding of u32 parts of uuid
This commit is contained in:
parent
5b52e2a50b
commit
965d457664
3 changed files with 196 additions and 55 deletions
|
@ -44,15 +44,15 @@
|
|||
[v]
|
||||
(= zero v))
|
||||
|
||||
#?(:clj
|
||||
(defn get-word-high
|
||||
[id]
|
||||
(.getMostSignificantBits ^UUID id)))
|
||||
(defn get-word-high
|
||||
[id]
|
||||
#?(:clj (.getMostSignificantBits ^UUID id)
|
||||
:cljs (impl/getHi (.-uuid ^UUID id))))
|
||||
|
||||
#?(:clj
|
||||
(defn get-word-low
|
||||
[id]
|
||||
(.getLeastSignificantBits ^UUID id)))
|
||||
(defn get-word-low
|
||||
[id]
|
||||
#?(:clj (.getLeastSignificantBits ^UUID id)
|
||||
:cljs (impl/getLo (.-uuid ^UUID id))))
|
||||
|
||||
(defn get-bytes
|
||||
[^UUID o]
|
||||
|
@ -80,12 +80,21 @@
|
|||
[id]
|
||||
(impl/shortV8 (dm/str id))))
|
||||
|
||||
#?(:cljs
|
||||
(defn get-unsigned-parts
|
||||
"Get a Uint32 array of length 4 that represents the UUID, needed
|
||||
for interact with wasm"
|
||||
[this]
|
||||
(impl/getUnsignedParts (.-uuid ^UUID this))))
|
||||
|
||||
|
||||
#?(:cljs
|
||||
(defn get-u32
|
||||
"A cached variant of get-unsigned-parts"
|
||||
[this]
|
||||
(let [buffer (unchecked-get this "__u32_buffer")]
|
||||
(if (nil? buffer)
|
||||
(let [buffer (impl/getUnsignedInt32Array (.-uuid ^UUID this))]
|
||||
(let [buffer (get-unsigned-parts (.-uuid ^UUID this))]
|
||||
(unchecked-set this "__u32_buffer" buffer)
|
||||
buffer)
|
||||
buffer))))
|
||||
|
@ -97,3 +106,33 @@
|
|||
b (.getLeastSignificantBits ^UUID id)]
|
||||
(+ (clojure.lang.Murmur3/hashLong a)
|
||||
(clojure.lang.Murmur3/hashLong b)))))
|
||||
|
||||
;; Commented code used for debug
|
||||
;; #?(:cljs
|
||||
;; (defn ^:export test-uuid
|
||||
;; []
|
||||
;; (let [expected #uuid "a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8"]
|
||||
;;
|
||||
;; (js/console.log "===> to-from-bytes-roundtrip")
|
||||
;; (js/console.log (uuid.impl/getBytes (str expected)))
|
||||
;; (js/console.log (uuid.impl/fromBytes (uuid.impl/getBytes (str expected))))
|
||||
;;
|
||||
;; (js/console.log "===> HI LO roundtrip")
|
||||
;; (let [hi (uuid.impl/getHi (str expected))
|
||||
;; lo (uuid.impl/getLo (str expected))
|
||||
;; res (uuid.impl/custom hi lo)]
|
||||
;;
|
||||
;; (js/console.log "HI:" hi)
|
||||
;; (js/console.log "LO:" lo)
|
||||
;; (js/console.log "RS:" res))
|
||||
;;
|
||||
;; (js/console.log "===> OTHER")
|
||||
;; (let [parts (uuid.impl/getUnsignedParts (str expected))
|
||||
;; res (uuid.impl/fromUnsignedParts (aget parts 0)
|
||||
;; (aget parts 1)
|
||||
;; (aget parts 2)
|
||||
;; (aget parts 3))]
|
||||
;; (js/console.log "PARTS:" parts)
|
||||
;; (js/console.log "RES: " res))
|
||||
;;
|
||||
;; )))
|
||||
|
|
|
@ -192,6 +192,76 @@ goog.scope(function() {
|
|||
}
|
||||
};
|
||||
|
||||
const fillBytes = (uuid) => {
|
||||
let rest;
|
||||
int8[0] = (rest = parseInt(uuid.slice(0, 8), 16)) >>> 24;
|
||||
int8[1] = (rest >>> 16) & 0xff;
|
||||
int8[2] = (rest >>> 8) & 0xff;
|
||||
int8[3] = rest & 0xff;
|
||||
|
||||
// Parse ........-####-....-....-............
|
||||
int8[4] = (rest = parseInt(uuid.slice(9, 13), 16)) >>> 8;
|
||||
int8[5] = rest & 0xff;
|
||||
|
||||
// Parse ........-....-####-....-............
|
||||
int8[6] = (rest = parseInt(uuid.slice(14, 18), 16)) >>> 8;
|
||||
int8[7] = rest & 0xff;
|
||||
|
||||
// Parse ........-....-....-####-............
|
||||
int8[8] = (rest = parseInt(uuid.slice(19, 23), 16)) >>> 8;
|
||||
int8[9] = rest & 0xff,
|
||||
|
||||
// Parse ........-....-....-....-############
|
||||
// (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes)
|
||||
int8[10] = ((rest = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff;
|
||||
int8[11] = (rest / 0x100000000) & 0xff;
|
||||
int8[12] = (rest >>> 24) & 0xff;
|
||||
int8[13] = (rest >>> 16) & 0xff;
|
||||
int8[14] = (rest >>> 8) & 0xff;
|
||||
int8[15] = rest & 0xff;
|
||||
}
|
||||
|
||||
const fromPair = (hi, lo) => {
|
||||
view.setBigInt64(0, hi);
|
||||
view.setBigInt64(8, lo);
|
||||
return encoding.bufferToHex(int8, true);
|
||||
}
|
||||
|
||||
const getHi = (uuid) => {
|
||||
fillBytes(uuid);
|
||||
return view.getBigInt64(0);
|
||||
}
|
||||
|
||||
const getLo = (uuid) => {
|
||||
fillBytes(uuid);
|
||||
return view.getBigInt64(8);
|
||||
}
|
||||
|
||||
const getBytes = (uuid) => {
|
||||
fillBytes(uuid);
|
||||
return Int8Array.from(int8);
|
||||
}
|
||||
|
||||
const getUnsignedParts = (uuid) => {
|
||||
fillBytes(uuid);
|
||||
const result = new Uint32Array(4);
|
||||
|
||||
result[0] = view.getUint32(0)
|
||||
result[1] = view.getUint32(4);
|
||||
result[2] = view.getUint32(8);
|
||||
result[3] = view.getUint32(12);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
const fromUnsignedParts = (a, b, c, d) => {
|
||||
view.setUint32(0, a)
|
||||
view.setUint32(4, b)
|
||||
view.setUint32(8, c)
|
||||
view.setUint32(12, d)
|
||||
return encoding.bufferToHex(int8, true);
|
||||
}
|
||||
|
||||
const fromArray = (u8data) => {
|
||||
int8.set(u8data);
|
||||
return encoding.bufferToHex(int8, true);
|
||||
|
@ -209,8 +279,14 @@ goog.scope(function() {
|
|||
};
|
||||
|
||||
factory.create = create;
|
||||
factory.setTag = setTag;
|
||||
factory.fromArray = fromArray;
|
||||
factory.fromPair = fromPair;
|
||||
factory.fromUnsignedParts = fromUnsignedParts;
|
||||
factory.getBytes = getBytes;
|
||||
factory.getHi = getHi;
|
||||
factory.getLo = getLo;
|
||||
factory.getUnsignedParts = getUnsignedParts;
|
||||
factory.setTag = setTag;
|
||||
return factory;
|
||||
})();
|
||||
|
||||
|
@ -220,67 +296,44 @@ goog.scope(function() {
|
|||
return encoding.bufferToBase62(short);
|
||||
};
|
||||
|
||||
self.custom = function formatAsUUID(mostSigBits, leastSigBits) {
|
||||
const most = mostSigBits.toString("16").padStart(16, "0");
|
||||
const least = leastSigBits.toString("16").padStart(16, "0");
|
||||
return `${most.substring(0, 8)}-${most.substring(8, 12)}-${most.substring(12)}-${least.substring(0, 4)}-${least.substring(4)}`;
|
||||
self.custom = function formatAsUUID(hi, lo) {
|
||||
if (!(hi instanceof BigInt)) {
|
||||
hi = BigInt(hi);
|
||||
}
|
||||
if (!(hi instanceof BigInt)) {
|
||||
lo = BigInt(lo);
|
||||
}
|
||||
|
||||
return self.v8.fromPair(hi, lo);
|
||||
};
|
||||
|
||||
self.fromBytes = function(data) {
|
||||
if (data instanceof Uint8Array) {
|
||||
return self.v8.fromArray(data);
|
||||
} else if (data instanceof Int8Array) {
|
||||
data = Uint8Array.from(data);
|
||||
return self.v8.fromArray(data);
|
||||
} else {
|
||||
let buffer = data?.buffer;
|
||||
if (buffer instanceof ArrayBuffer) {
|
||||
data = new Uint8Array(buffer);
|
||||
return self.v8.fromArray(data);
|
||||
} else {
|
||||
throw new Error("invalid array type received");
|
||||
}
|
||||
throw new Error("invalid array type received");
|
||||
}
|
||||
};
|
||||
|
||||
// Code based from uuidjs/parse.ts
|
||||
self.getBytes = function parse(uuid) {
|
||||
const buffer = new ArrayBuffer(16);
|
||||
const view = new Int8Array(buffer);
|
||||
let rest;
|
||||
return self.v8.getBytes(uuid);
|
||||
};
|
||||
|
||||
// Parse ########-....-....-....-............
|
||||
view[0] = (rest = parseInt(uuid.slice(0, 8), 16)) >>> 24;
|
||||
view[1] = (rest >>> 16) & 0xff;
|
||||
view[2] = (rest >>> 8) & 0xff;
|
||||
view[3] = rest & 0xff;
|
||||
self.getUnsignedParts = function (uuid) {
|
||||
return self.v8.getUnsignedParts(uuid);
|
||||
};
|
||||
|
||||
// Parse ........-####-....-....-............
|
||||
view[4] = (rest = parseInt(uuid.slice(9, 13), 16)) >>> 8;
|
||||
view[5] = rest & 0xff;
|
||||
self.fromUnsignedParts = function(a,b,c,d) {
|
||||
return self.v8.fromUnsignedParts(a,b,c,d);
|
||||
};
|
||||
|
||||
// Parse ........-....-####-....-............
|
||||
view[6] = (rest = parseInt(uuid.slice(14, 18), 16)) >>> 8;
|
||||
view[7] = rest & 0xff;
|
||||
|
||||
// Parse ........-....-....-####-............
|
||||
view[8] = (rest = parseInt(uuid.slice(19, 23), 16)) >>> 8;
|
||||
view[9] = rest & 0xff,
|
||||
|
||||
// Parse ........-....-....-....-############
|
||||
// (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes)
|
||||
view[10] = ((rest = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000) & 0xff;
|
||||
view[11] = (rest / 0x100000000) & 0xff;
|
||||
view[12] = (rest >>> 24) & 0xff;
|
||||
view[13] = (rest >>> 16) & 0xff;
|
||||
view[14] = (rest >>> 8) & 0xff;
|
||||
view[15] = rest & 0xff;
|
||||
|
||||
return view;
|
||||
self.getHi = function (uuid) {
|
||||
return self.v8.getHi(uuid);
|
||||
}
|
||||
|
||||
self.getUnsignedInt32Array = function (uuid) {
|
||||
const bytes = self.getBytes(uuid);
|
||||
return new Uint32Array(bytes.buffer);
|
||||
self.getLo = function (uuid) {
|
||||
return self.v8.getLo(uuid);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -43,5 +43,54 @@
|
|||
(t/is (= result uuid))))))
|
||||
|
||||
|
||||
(t/deftest bytes-roundtrip-2
|
||||
(let [uuid (uuid/uuid "a1a2a3a4-b1b2-c1c2-d1d2-d3d4d5d6d7d8")
|
||||
result-bytes (uuid/get-bytes uuid)
|
||||
expected-hi #?(:clj -6799692559624781374
|
||||
:cljs (js/BigInt "-6799692559624781374"))
|
||||
expected-lo #?(:clj -3327364263599220776
|
||||
:cljs (js/BigInt "-3327364263599220776"))
|
||||
|
||||
expected-bytes [-95, -94, -93, -92, -79, -78, -63, -62, -47, -46, -45, -44, -43, -42, -41, -40]]
|
||||
|
||||
(t/testing "get-bytes"
|
||||
(let [data (uuid/get-bytes uuid)]
|
||||
(t/is (= (nth expected-bytes 0) (aget data 0)))
|
||||
(t/is (= (nth expected-bytes 1) (aget data 1)))
|
||||
(t/is (= (nth expected-bytes 2) (aget data 2)))
|
||||
(t/is (= (nth expected-bytes 3) (aget data 3)))
|
||||
(t/is (= (nth expected-bytes 4) (aget data 4)))
|
||||
(t/is (= (nth expected-bytes 5) (aget data 5)))
|
||||
(t/is (= (nth expected-bytes 6) (aget data 6)))
|
||||
(t/is (= (nth expected-bytes 7) (aget data 7)))
|
||||
(t/is (= (nth expected-bytes 8) (aget data 8)))
|
||||
(t/is (= (nth expected-bytes 9) (aget data 9)))
|
||||
(t/is (= (nth expected-bytes 10) (aget data 10)))
|
||||
(t/is (= (nth expected-bytes 11) (aget data 11)))
|
||||
(t/is (= (nth expected-bytes 12) (aget data 12)))
|
||||
(t/is (= (nth expected-bytes 13) (aget data 13)))
|
||||
(t/is (= (nth expected-bytes 14) (aget data 14)))
|
||||
(t/is (= (nth expected-bytes 15) (aget data 15)))))
|
||||
|
||||
(t/testing "from-bytes"
|
||||
(let [data (create-array expected-bytes)
|
||||
result (uuid/from-bytes data)]
|
||||
(t/is (= result uuid))))
|
||||
|
||||
(t/testing "hi-low"
|
||||
(let [hi (uuid/get-word-high uuid)
|
||||
lo (uuid/get-word-low uuid)]
|
||||
|
||||
(t/is (= hi expected-hi))
|
||||
(t/is (= lo expected-lo))))
|
||||
|
||||
#?(:cljs
|
||||
(t/testing "unsigned-parts"
|
||||
(let [parts (uuid/get-unsigned-parts uuid)
|
||||
expected [2711790500, 2981282242, 3520254932, 3587626968]]
|
||||
|
||||
(t/is (instance? js/Uint32Array parts))
|
||||
(t/is (= (nth expected 0) (aget parts 0)))
|
||||
(t/is (= (nth expected 1) (aget parts 1)))
|
||||
(t/is (= (nth expected 2) (aget parts 2)))
|
||||
(t/is (= (nth expected 3) (aget parts 3))))))))
|
||||
|
|
Loading…
Reference in a new issue