plum
git clone https://git.pyrossh.dev/plum
A statically typed, imperative programming language inspired by rust, python
872454e
— Peter John
2026-11-01T22:20:50+05:30
perf: bytesEqual intrinsic for Str.eq
- plum-std/ByteSlice.plum +23 -0
- plum-std/Str.plum +1 -8
- plum-wasm-codegen/src/lib.rs +50 -0
plum-std/ByteSlice.plum
CHANGED
|
@@ -37,6 +37,11 @@ fun makeBytes(n: Int) -> []Byte =
|
|
|
37
37
|
fun copyBytes(dst: []Byte, dstStart: Int, src: []Byte, srcStart: Int, n: Int) -> Unit =
|
|
38
38
|
todo
|
|
39
39
|
|
|
40
|
+
# True when `a[aStart..aStart+n]` and `b[bStart..bStart+n]` hold the same
|
|
41
|
+
# bytes (the memcmp analog; `n == 0` is always true). Traps if out of range.
|
|
42
|
+
fun bytesEqual(a: []Byte, aStart: Int, b: []Byte, bStart: Int, n: Int) -> Bool =
|
|
43
|
+
todo
|
|
44
|
+
|
|
40
45
|
# Like `copyBytes`, but the source is a `Str` — ordinary Plum glue over
|
|
41
46
|
# `copyBytes` itself, reaching through `Str`'s `bytes: []Byte` field.
|
|
42
47
|
fun copyStrToBytes(dst: []Byte, dstStart: Int, src: Str, srcStart: Int, n: Int) -> Unit =
|
|
@@ -50,3 +55,21 @@ fun bytesToStr(src: []Byte, start: Int, n: Int) -> Str =
|
|
|
50
55
|
data := makeBytes(n)
|
|
51
56
|
copyBytes(data, 0, src, start, n)
|
|
52
57
|
return Str(bytes: data)
|
|
58
|
+
|
|
59
|
+
fun testBytesOf(s: Str) -> []Byte =
|
|
60
|
+
out := makeBytes(s.length())
|
|
61
|
+
copyStrToBytes(out, 0, s, 0, s.length())
|
|
62
|
+
return out
|
|
63
|
+
|
|
64
|
+
test "bytesEqual: equal, differ at first, differ at last, n == 0, offsets"
|
|
65
|
+
a := testBytesOf("hello world")
|
|
66
|
+
b := testBytesOf("hello world")
|
|
67
|
+
assert bytesEqual(a, 0, b, 0, 11)
|
|
68
|
+
c := testBytesOf("jello world")
|
|
69
|
+
assert !bytesEqual(a, 0, c, 0, 11)
|
|
70
|
+
d := testBytesOf("hello worlD")
|
|
71
|
+
assert !bytesEqual(a, 0, d, 0, 11)
|
|
72
|
+
assert bytesEqual(a, 0, d, 0, 10)
|
|
73
|
+
assert bytesEqual(a, 3, c, 3, 0)
|
|
74
|
+
assert bytesEqual(a, 6, testBytesOf("xxworld"), 2, 5)
|
|
75
|
+
assert !bytesEqual(a, 6, testBytesOf("xxwurld"), 2, 5)
|
plum-std/Str.plum
CHANGED
|
@@ -156,14 +156,7 @@ record Str(bytes: []Byte) =
|
|
|
156
156
|
n := self.length()
|
|
157
157
|
if n != o.length()
|
|
158
158
|
return false
|
|
159
|
-
a := self.bytes
|
|
160
|
-
b := o.bytes
|
|
161
|
-
i := 0
|
|
162
|
-
while i < n
|
|
163
|
-
|
|
159
|
+
return bytesEqual(self.bytes, 0, o.bytes, 0, n)
|
|
164
|
-
return false
|
|
165
|
-
i = i + 1
|
|
166
|
-
return true
|
|
167
160
|
|
|
168
161
|
fun toStr(self) -> Str =
|
|
169
162
|
self
|
plum-wasm-codegen/src/lib.rs
CHANGED
|
@@ -2706,6 +2706,56 @@ fn compileIntrinsicFnBody(f: &ast::Fn, subst: &Subst) -> Option<Vec<u8>> {
|
|
|
2706
2706
|
Instruction::End.encode(&mut body);
|
|
2707
2707
|
Some(body)
|
|
2708
2708
|
}
|
|
2709
|
+
// bytesEqual(a: []Byte, aStart: Int, b: []Byte, bStart: Int, n: Int) -> Bool
|
|
2710
|
+
// — the memcmp analog: a tight loop with early exit on mismatch.
|
|
2711
|
+
// Locals: 5 = i (i32), 6 = n (i32), 7 = aStart (i32), 8 = bStart (i32).
|
|
2712
|
+
(None, "bytesEqual") => {
|
|
2713
|
+
let mut body = vec![1u8, 4u8, 0x7f];
|
|
2714
|
+
let wrap = |b: &mut Vec<u8>, l: u32| {
|
|
2715
|
+
Instruction::LocalGet(l).encode(b);
|
|
2716
|
+
Instruction::I32WrapI64.encode(b);
|
|
2717
|
+
};
|
|
2718
|
+
wrap(&mut body, 4);
|
|
2719
|
+
Instruction::LocalSet(6).encode(&mut body);
|
|
2720
|
+
wrap(&mut body, 1);
|
|
2721
|
+
Instruction::LocalSet(7).encode(&mut body);
|
|
2722
|
+
wrap(&mut body, 3);
|
|
2723
|
+
Instruction::LocalSet(8).encode(&mut body);
|
|
2724
|
+
Instruction::Block(BlockType::Result(ValType::I32)).encode(&mut body);
|
|
2725
|
+
Instruction::Loop(BlockType::Empty).encode(&mut body);
|
|
2726
|
+
Instruction::LocalGet(5).encode(&mut body);
|
|
2727
|
+
Instruction::LocalGet(6).encode(&mut body);
|
|
2728
|
+
Instruction::I32GeS.encode(&mut body);
|
|
2729
|
+
Instruction::If(BlockType::Empty).encode(&mut body);
|
|
2730
|
+
Instruction::I32Const(1).encode(&mut body);
|
|
2731
|
+
Instruction::Br(2).encode(&mut body);
|
|
2732
|
+
Instruction::End.encode(&mut body);
|
|
2733
|
+
Instruction::LocalGet(0).encode(&mut body);
|
|
2734
|
+
Instruction::LocalGet(7).encode(&mut body);
|
|
2735
|
+
Instruction::LocalGet(5).encode(&mut body);
|
|
2736
|
+
Instruction::I32Add.encode(&mut body);
|
|
2737
|
+
Instruction::ArrayGetU(byte_array_type_idx).encode(&mut body);
|
|
2738
|
+
Instruction::LocalGet(2).encode(&mut body);
|
|
2739
|
+
Instruction::LocalGet(8).encode(&mut body);
|
|
2740
|
+
Instruction::LocalGet(5).encode(&mut body);
|
|
2741
|
+
Instruction::I32Add.encode(&mut body);
|
|
2742
|
+
Instruction::ArrayGetU(byte_array_type_idx).encode(&mut body);
|
|
2743
|
+
Instruction::I32Ne.encode(&mut body);
|
|
2744
|
+
Instruction::If(BlockType::Empty).encode(&mut body);
|
|
2745
|
+
Instruction::I32Const(0).encode(&mut body);
|
|
2746
|
+
Instruction::Br(2).encode(&mut body);
|
|
2747
|
+
Instruction::End.encode(&mut body);
|
|
2748
|
+
Instruction::LocalGet(5).encode(&mut body);
|
|
2749
|
+
Instruction::I32Const(1).encode(&mut body);
|
|
2750
|
+
Instruction::I32Add.encode(&mut body);
|
|
2751
|
+
Instruction::LocalSet(5).encode(&mut body);
|
|
2752
|
+
Instruction::Br(0).encode(&mut body);
|
|
2753
|
+
Instruction::End.encode(&mut body); // loop
|
|
2754
|
+
Instruction::Unreachable.encode(&mut body);
|
|
2755
|
+
Instruction::End.encode(&mut body); // block
|
|
2756
|
+
Instruction::End.encode(&mut body); // function
|
|
2757
|
+
Some(body)
|
|
2758
|
+
}
|
|
2709
2759
|
_ => None,
|
|
2710
2760
|
}
|
|
2711
2761
|
}
|