authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-03-26 19:47:26+13:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-04-11 19:36:35+12:00
logea1d2a240956b2b25d945d361fd274e67ec7849c
tree0a2d39b6b4d1d2cc96053e39b57bde9abc9181b6
parente309ad884a5d2fc8b78325199cd6e81d2efa220d

Add read-only, non-allocating Int for internal constants

A constant Int is one which has a value of null for its allocator field. It cannot be resized or have its limbs written. Any attempt made to write to it will be caught with a runtime panic.

1 files changed, 83 insertions(+), 63 deletions(-)

std/math/big/int.zig+83-63
......@@ -22,7 +22,7 @@ comptime {
2222}
2323
2424pub const Int = struct {
25 allocator: *Allocator,
25 allocator: ?*Allocator,
2626 positive: bool,
2727 // - little-endian ordered
2828 // - len >= 1 always
......@@ -55,16 +55,40 @@ pub const Int = struct {
5555 };
5656 }
5757
58 // Initialize an Int directly from a fixed set of limb values. This is considered read-only
59 // and cannot be used as a receiver argument to any functions. If this tries to allocate
60 // at any point a panic will occur due to the null allocator.
61 pub fn initFixed(limbs: []const Limb) Int {
62 var self = Int{
63 .allocator = null,
64 .positive = true,
65 // Cast away the const, invalid use to pass as a pointer argument.
66 .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len],
67 .len = limbs.len,
68 };
69
70 self.normN(limbs.len);
71 return self;
72 }
73
5874 pub fn ensureCapacity(self: *Int, capacity: usize) !void {
75 self.assertWritable();
5976 if (capacity <= self.limbs.len) {
6077 return;
6178 }
6279
63 self.limbs = try self.allocator.realloc(self.limbs, capacity);
80 self.limbs = try self.allocator.?.realloc(self.limbs, capacity);
81 }
82
83 fn assertWritable(self: Int) void {
84 if (self.allocator == null) {
85 @panic("provided Int value is read-only but must be writable");
86 }
6487 }
6588
6689 pub fn deinit(self: *Int) void {
67 self.allocator.free(self.limbs);
90 self.assertWritable();
91 self.allocator.?.free(self.limbs);
6892 self.* = undefined;
6993 }
7094
......@@ -73,7 +97,7 @@ pub const Int = struct {
7397 .allocator = other.allocator,
7498 .positive = other.positive,
7599 .limbs = block: {
76 var limbs = try other.allocator.alloc(Limb, other.len);
100 var limbs = try other.allocator.?.alloc(Limb, other.len);
77101 mem.copy(Limb, limbs[0..], other.limbs[0..other.len]);
78102 break :block limbs;
79103 },
......@@ -82,7 +106,8 @@ pub const Int = struct {
82106 }
83107
84108 pub fn copy(self: *Int, other: Int) !void {
85 if (self == &other) {
109 self.assertWritable();
110 if (self.limbs.ptr == other.limbs.ptr) {
86111 return;
87112 }
88113
......@@ -93,6 +118,7 @@ pub const Int = struct {
93118 }
94119
95120 pub fn swap(self: *Int, other: *Int) void {
121 self.assertWritable();
96122 mem.swap(Int, self, other);
97123 }
98124
......@@ -103,20 +129,20 @@ pub const Int = struct {
103129 debug.warn("\n");
104130 }
105131
106 pub fn negate(r: *Int) void {
107 r.positive = !r.positive;
132 pub fn negate(self: *Int) void {
133 self.positive = !self.positive;
108134 }
109135
110 pub fn abs(r: *Int) void {
111 r.positive = true;
136 pub fn abs(self: *Int) void {
137 self.positive = true;
112138 }
113139
114 pub fn isOdd(r: Int) bool {
115 return r.limbs[0] & 1 != 0;
140 pub fn isOdd(self: Int) bool {
141 return self.limbs[0] & 1 != 0;
116142 }
117143
118 pub fn isEven(r: Int) bool {
119 return !r.isOdd();
144 pub fn isEven(self: Int) bool {
145 return !self.isOdd();
120146 }
121147
122148 // Returns the number of bits required to represent the absolute value of self.
......@@ -179,6 +205,7 @@ pub const Int = struct {
179205 }
180206
181207 pub fn set(self: *Int, value: var) Allocator.Error!void {
208 self.assertWritable();
182209 const T = @typeOf(value);
183210
184211 switch (@typeInfo(T)) {
......@@ -304,6 +331,7 @@ pub const Int = struct {
304331 }
305332
306333 pub fn setString(self: *Int, base: u8, value: []const u8) !void {
334 self.assertWritable();
307335 if (base < 2 or base > 16) {
308336 return error.InvalidBase;
309337 }
......@@ -315,23 +343,16 @@ pub const Int = struct {
315343 i += 1;
316344 }
317345
318 // TODO values less than limb size should guarantee non allocating
319 var base_buffer: [512]u8 = undefined;
320 const base_al = &std.heap.FixedBufferAllocator.init(base_buffer[0..]).allocator;
321 const base_ap = try Int.initSet(base_al, base);
322
323 var d_buffer: [512]u8 = undefined;
324 var d_fba = std.heap.FixedBufferAllocator.init(d_buffer[0..]);
325 const d_al = &d_fba.allocator;
326
346 const ap_base = Int.initFixed(([]Limb{base})[0..]);
327347 try self.set(0);
348
328349 for (value[i..]) |ch| {
329350 const d = try charToDigit(ch, base);
330 d_fba.end_index = 0;
331 const d_ap = try Int.initSet(d_al, d);
332351
333 try self.mul(self.*, base_ap);
334 try self.add(self.*, d_ap);
352 const ap_d = Int.initFixed(([]Limb{d})[0..]);
353
354 try self.mul(self.*, ap_base);
355 try self.add(self.*, ap_d);
335356 }
336357 self.positive = positive;
337358 }
......@@ -520,8 +541,19 @@ pub const Int = struct {
520541 r.len = if (j != 0) j else 1;
521542 }
522543
544 // Cannot be used as a result argument to any function.
545 fn readOnlyPositive(a: Int) Int {
546 return Int{
547 .allocator = null,
548 .positive = true,
549 .limbs = a.limbs,
550 .len = a.len,
551 };
552 }
553
523554 // r = a + b
524555 pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {
556 r.assertWritable();
525557 if (a.eqZero()) {
526558 try r.copy(b);
527559 return;
......@@ -533,22 +565,10 @@ pub const Int = struct {
533565 if (a.positive != b.positive) {
534566 if (a.positive) {
535567 // (a) + (-b) => a - b
536 const bp = Int{
537 .allocator = undefined,
538 .positive = true,
539 .limbs = b.limbs,
540 .len = b.len,
541 };
542 try r.sub(a, bp);
568 try r.sub(a, readOnlyPositive(b));
543569 } else {
544570 // (-a) + (b) => b - a
545 const ap = Int{
546 .allocator = undefined,
547 .positive = true,
548 .limbs = a.limbs,
549 .len = a.len,
550 };
551 try r.sub(b, ap);
571 try r.sub(b, readOnlyPositive(a));
552572 }
553573 } else {
554574 if (a.len >= b.len) {
......@@ -591,25 +611,14 @@ pub const Int = struct {
591611
592612 // r = a - b
593613 pub fn sub(r: *Int, a: Int, b: Int) !void {
614 r.assertWritable();
594615 if (a.positive != b.positive) {
595616 if (a.positive) {
596617 // (a) - (-b) => a + b
597 const bp = Int{
598 .allocator = undefined,
599 .positive = true,
600 .limbs = b.limbs,
601 .len = b.len,
602 };
603 try r.add(a, bp);
618 try r.add(a, readOnlyPositive(b));
604619 } else {
605620 // (-a) - (b) => -(a + b)
606 const ap = Int{
607 .allocator = undefined,
608 .positive = true,
609 .limbs = a.limbs,
610 .len = a.len,
611 };
612 try r.add(ap, b);
621 try r.add(readOnlyPositive(a), b);
613622 r.positive = false;
614623 }
615624 } else {
......@@ -671,12 +680,14 @@ pub const Int = struct {
671680 //
672681 // For greatest efficiency, ensure rma does not alias a or b.
673682 pub fn mul(rma: *Int, a: Int, b: Int) !void {
683 rma.assertWritable();
684
674685 var r = rma;
675686 var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;
676687
677688 var sr: Int = undefined;
678689 if (aliased) {
679 sr = try Int.initCapacity(rma.allocator, a.len + b.len);
690 sr = try Int.initCapacity(rma.allocator.?, a.len + b.len);
680691 r = &sr;
681692 aliased = true;
682693 }
......@@ -745,13 +756,9 @@ pub const Int = struct {
745756
746757 // Trunc -> Floor.
747758 if (!q.positive) {
748 // TODO values less than limb size should guarantee non allocating
749 var one_buffer: [512]u8 = undefined;
750 const one_al = &std.heap.FixedBufferAllocator.init(one_buffer[0..]).allocator;
751 const one_ap = try Int.initSet(one_al, 1);
752
753 try q.sub(q.*, one_ap);
754 try r.add(q.*, one_ap);
759 const one = Int.initFixed(([]Limb{1})[0..]);
760 try q.sub(q.*, one);
761 try r.add(q.*, one);
755762 }
756763 r.positive = b.positive;
757764 }
......@@ -763,6 +770,9 @@ pub const Int = struct {
763770
764771 // Truncates by default.
765772 fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void {
773 quo.assertWritable();
774 rem.assertWritable();
775
766776 if (b.eqZero()) {
767777 @panic("division by zero");
768778 }
......@@ -800,7 +810,7 @@ pub const Int = struct {
800810
801811 // x may grow one limb during normalization
802812 try quo.ensureCapacity(a.len + y.len);
803 try divN(quo.allocator, quo, rem, &x, &y);
813 try divN(quo.allocator.?, quo, rem, &x, &y);
804814
805815 quo.positive = a.positive == b.positive;
806816 }
......@@ -919,6 +929,8 @@ pub const Int = struct {
919929
920930 // r = a << shift, in other words, r = a * 2^shift
921931 pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {
932 r.assertWritable();
933
922934 try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1);
923935 llshl(r.limbs[0..], a.limbs[0..a.len], shift);
924936 r.norm1(a.len + (shift / Limb.bit_count) + 1);
......@@ -950,6 +962,8 @@ pub const Int = struct {
950962
951963 // r = a >> shift
952964 pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {
965 r.assertWritable();
966
953967 if (a.len <= shift / Limb.bit_count) {
954968 r.len = 1;
955969 r.limbs[0] = 0;
......@@ -985,6 +999,8 @@ pub const Int = struct {
985999
9861000 // r = a | b
9871001 pub fn bitOr(r: *Int, a: Int, b: Int) !void {
1002 r.assertWritable();
1003
9881004 if (a.len > b.len) {
9891005 try r.ensureCapacity(a.len);
9901006 llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
......@@ -1012,6 +1028,8 @@ pub const Int = struct {
10121028
10131029 // r = a & b
10141030 pub fn bitAnd(r: *Int, a: Int, b: Int) !void {
1031 r.assertWritable();
1032
10151033 if (a.len > b.len) {
10161034 try r.ensureCapacity(b.len);
10171035 lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
......@@ -1036,6 +1054,8 @@ pub const Int = struct {
10361054
10371055 // r = a ^ b
10381056 pub fn bitXor(r: *Int, a: Int, b: Int) !void {
1057 r.assertWritable();
1058
10391059 if (a.len > b.len) {
10401060 try r.ensureCapacity(a.len);
10411061 llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);