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 {...@@ -22,7 +22,7 @@ comptime {
22}22}
2323
24pub const Int = struct {24pub const Int = struct {
25 allocator: *Allocator,25 allocator: ?*Allocator,
26 positive: bool,26 positive: bool,
27 // - little-endian ordered27 // - little-endian ordered
28 // - len >= 1 always28 // - len >= 1 always
...@@ -55,16 +55,40 @@ pub const Int = struct {...@@ -55,16 +55,40 @@ pub const Int = struct {
55 };55 };
56 }56 }
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
58 pub fn ensureCapacity(self: *Int, capacity: usize) !void {74 pub fn ensureCapacity(self: *Int, capacity: usize) !void {
75 self.assertWritable();
59 if (capacity <= self.limbs.len) {76 if (capacity <= self.limbs.len) {
60 return;77 return;
61 }78 }
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 }
64 }87 }
6588
66 pub fn deinit(self: *Int) void {89 pub fn deinit(self: *Int) void {
67 self.allocator.free(self.limbs);90 self.assertWritable();
91 self.allocator.?.free(self.limbs);
68 self.* = undefined;92 self.* = undefined;
69 }93 }
7094
...@@ -73,7 +97,7 @@ pub const Int = struct {...@@ -73,7 +97,7 @@ pub const Int = struct {
73 .allocator = other.allocator,97 .allocator = other.allocator,
74 .positive = other.positive,98 .positive = other.positive,
75 .limbs = block: {99 .limbs = block: {
76 var limbs = try other.allocator.alloc(Limb, other.len);100 var limbs = try other.allocator.?.alloc(Limb, other.len);
77 mem.copy(Limb, limbs[0..], other.limbs[0..other.len]);101 mem.copy(Limb, limbs[0..], other.limbs[0..other.len]);
78 break :block limbs;102 break :block limbs;
79 },103 },
...@@ -82,7 +106,8 @@ pub const Int = struct {...@@ -82,7 +106,8 @@ pub const Int = struct {
82 }106 }
83107
84 pub fn copy(self: *Int, other: Int) !void {108 pub fn copy(self: *Int, other: Int) !void {
85 if (self == &other) {109 self.assertWritable();
110 if (self.limbs.ptr == other.limbs.ptr) {
86 return;111 return;
87 }112 }
88113
...@@ -93,6 +118,7 @@ pub const Int = struct {...@@ -93,6 +118,7 @@ pub const Int = struct {
93 }118 }
94119
95 pub fn swap(self: *Int, other: *Int) void {120 pub fn swap(self: *Int, other: *Int) void {
121 self.assertWritable();
96 mem.swap(Int, self, other);122 mem.swap(Int, self, other);
97 }123 }
98124
...@@ -103,20 +129,20 @@ pub const Int = struct {...@@ -103,20 +129,20 @@ pub const Int = struct {
103 debug.warn("\n");129 debug.warn("\n");
104 }130 }
105131
106 pub fn negate(r: *Int) void {132 pub fn negate(self: *Int) void {
107 r.positive = !r.positive;133 self.positive = !self.positive;
108 }134 }
109135
110 pub fn abs(r: *Int) void {136 pub fn abs(self: *Int) void {
111 r.positive = true;137 self.positive = true;
112 }138 }
113139
114 pub fn isOdd(r: Int) bool {140 pub fn isOdd(self: Int) bool {
115 return r.limbs[0] & 1 != 0;141 return self.limbs[0] & 1 != 0;
116 }142 }
117143
118 pub fn isEven(r: Int) bool {144 pub fn isEven(self: Int) bool {
119 return !r.isOdd();145 return !self.isOdd();
120 }146 }
121147
122 // Returns the number of bits required to represent the absolute value of self.148 // Returns the number of bits required to represent the absolute value of self.
...@@ -179,6 +205,7 @@ pub const Int = struct {...@@ -179,6 +205,7 @@ pub const Int = struct {
179 }205 }
180206
181 pub fn set(self: *Int, value: var) Allocator.Error!void {207 pub fn set(self: *Int, value: var) Allocator.Error!void {
208 self.assertWritable();
182 const T = @typeOf(value);209 const T = @typeOf(value);
183210
184 switch (@typeInfo(T)) {211 switch (@typeInfo(T)) {
...@@ -304,6 +331,7 @@ pub const Int = struct {...@@ -304,6 +331,7 @@ pub const Int = struct {
304 }331 }
305332
306 pub fn setString(self: *Int, base: u8, value: []const u8) !void {333 pub fn setString(self: *Int, base: u8, value: []const u8) !void {
334 self.assertWritable();
307 if (base < 2 or base > 16) {335 if (base < 2 or base > 16) {
308 return error.InvalidBase;336 return error.InvalidBase;
309 }337 }
...@@ -315,23 +343,16 @@ pub const Int = struct {...@@ -315,23 +343,16 @@ pub const Int = struct {
315 i += 1;343 i += 1;
316 }344 }
317345
318 // TODO values less than limb size should guarantee non allocating346 const ap_base = Int.initFixed(([]Limb{base})[0..]);
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
327 try self.set(0);347 try self.set(0);
348
328 for (value[i..]) |ch| {349 for (value[i..]) |ch| {
329 const d = try charToDigit(ch, base);350 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);352 const ap_d = Int.initFixed(([]Limb{d})[0..]);
334 try self.add(self.*, d_ap);353
354 try self.mul(self.*, ap_base);
355 try self.add(self.*, ap_d);
335 }356 }
336 self.positive = positive;357 self.positive = positive;
337 }358 }
...@@ -520,8 +541,19 @@ pub const Int = struct {...@@ -520,8 +541,19 @@ pub const Int = struct {
520 r.len = if (j != 0) j else 1;541 r.len = if (j != 0) j else 1;
521 }542 }
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
523 // r = a + b554 // r = a + b
524 pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {555 pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {
556 r.assertWritable();
525 if (a.eqZero()) {557 if (a.eqZero()) {
526 try r.copy(b);558 try r.copy(b);
527 return;559 return;
...@@ -533,22 +565,10 @@ pub const Int = struct {...@@ -533,22 +565,10 @@ pub const Int = struct {
533 if (a.positive != b.positive) {565 if (a.positive != b.positive) {
534 if (a.positive) {566 if (a.positive) {
535 // (a) + (-b) => a - b567 // (a) + (-b) => a - b
536 const bp = Int{568 try r.sub(a, readOnlyPositive(b));
537 .allocator = undefined,
538 .positive = true,
539 .limbs = b.limbs,
540 .len = b.len,
541 };
542 try r.sub(a, bp);
543 } else {569 } else {
544 // (-a) + (b) => b - a570 // (-a) + (b) => b - a
545 const ap = Int{571 try r.sub(b, readOnlyPositive(a));
546 .allocator = undefined,
547 .positive = true,
548 .limbs = a.limbs,
549 .len = a.len,
550 };
551 try r.sub(b, ap);
552 }572 }
553 } else {573 } else {
554 if (a.len >= b.len) {574 if (a.len >= b.len) {
...@@ -591,25 +611,14 @@ pub const Int = struct {...@@ -591,25 +611,14 @@ pub const Int = struct {
591611
592 // r = a - b612 // r = a - b
593 pub fn sub(r: *Int, a: Int, b: Int) !void {613 pub fn sub(r: *Int, a: Int, b: Int) !void {
614 r.assertWritable();
594 if (a.positive != b.positive) {615 if (a.positive != b.positive) {
595 if (a.positive) {616 if (a.positive) {
596 // (a) - (-b) => a + b617 // (a) - (-b) => a + b
597 const bp = Int{618 try r.add(a, readOnlyPositive(b));
598 .allocator = undefined,
599 .positive = true,
600 .limbs = b.limbs,
601 .len = b.len,
602 };
603 try r.add(a, bp);
604 } else {619 } else {
605 // (-a) - (b) => -(a + b)620 // (-a) - (b) => -(a + b)
606 const ap = Int{621 try r.add(readOnlyPositive(a), b);
607 .allocator = undefined,
608 .positive = true,
609 .limbs = a.limbs,
610 .len = a.len,
611 };
612 try r.add(ap, b);
613 r.positive = false;622 r.positive = false;
614 }623 }
615 } else {624 } else {
...@@ -671,12 +680,14 @@ pub const Int = struct {...@@ -671,12 +680,14 @@ pub const Int = struct {
671 //680 //
672 // For greatest efficiency, ensure rma does not alias a or b.681 // For greatest efficiency, ensure rma does not alias a or b.
673 pub fn mul(rma: *Int, a: Int, b: Int) !void {682 pub fn mul(rma: *Int, a: Int, b: Int) !void {
683 rma.assertWritable();
684
674 var r = rma;685 var r = rma;
675 var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;686 var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;
676687
677 var sr: Int = undefined;688 var sr: Int = undefined;
678 if (aliased) {689 if (aliased) {
679 sr = try Int.initCapacity(rma.allocator, a.len + b.len);690 sr = try Int.initCapacity(rma.allocator.?, a.len + b.len);
680 r = &sr;691 r = &sr;
681 aliased = true;692 aliased = true;
682 }693 }
...@@ -745,13 +756,9 @@ pub const Int = struct {...@@ -745,13 +756,9 @@ pub const Int = struct {
745756
746 // Trunc -> Floor.757 // Trunc -> Floor.
747 if (!q.positive) {758 if (!q.positive) {
748 // TODO values less than limb size should guarantee non allocating759 const one = Int.initFixed(([]Limb{1})[0..]);
749 var one_buffer: [512]u8 = undefined;760 try q.sub(q.*, one);
750 const one_al = &std.heap.FixedBufferAllocator.init(one_buffer[0..]).allocator;761 try r.add(q.*, one);
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);
755 }762 }
756 r.positive = b.positive;763 r.positive = b.positive;
757 }764 }
...@@ -763,6 +770,9 @@ pub const Int = struct {...@@ -763,6 +770,9 @@ pub const Int = struct {
763770
764 // Truncates by default.771 // Truncates by default.
765 fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void {772 fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void {
773 quo.assertWritable();
774 rem.assertWritable();
775
766 if (b.eqZero()) {776 if (b.eqZero()) {
767 @panic("division by zero");777 @panic("division by zero");
768 }778 }
...@@ -800,7 +810,7 @@ pub const Int = struct {...@@ -800,7 +810,7 @@ pub const Int = struct {
800810
801 // x may grow one limb during normalization811 // x may grow one limb during normalization
802 try quo.ensureCapacity(a.len + y.len);812 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
805 quo.positive = a.positive == b.positive;815 quo.positive = a.positive == b.positive;
806 }816 }
...@@ -919,6 +929,8 @@ pub const Int = struct {...@@ -919,6 +929,8 @@ pub const Int = struct {
919929
920 // r = a << shift, in other words, r = a * 2^shift930 // r = a << shift, in other words, r = a * 2^shift
921 pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {931 pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {
932 r.assertWritable();
933
922 try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1);934 try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1);
923 llshl(r.limbs[0..], a.limbs[0..a.len], shift);935 llshl(r.limbs[0..], a.limbs[0..a.len], shift);
924 r.norm1(a.len + (shift / Limb.bit_count) + 1);936 r.norm1(a.len + (shift / Limb.bit_count) + 1);
...@@ -950,6 +962,8 @@ pub const Int = struct {...@@ -950,6 +962,8 @@ pub const Int = struct {
950962
951 // r = a >> shift963 // r = a >> shift
952 pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {964 pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {
965 r.assertWritable();
966
953 if (a.len <= shift / Limb.bit_count) {967 if (a.len <= shift / Limb.bit_count) {
954 r.len = 1;968 r.len = 1;
955 r.limbs[0] = 0;969 r.limbs[0] = 0;
...@@ -985,6 +999,8 @@ pub const Int = struct {...@@ -985,6 +999,8 @@ pub const Int = struct {
985999
986 // r = a | b1000 // r = a | b
987 pub fn bitOr(r: *Int, a: Int, b: Int) !void {1001 pub fn bitOr(r: *Int, a: Int, b: Int) !void {
1002 r.assertWritable();
1003
988 if (a.len > b.len) {1004 if (a.len > b.len) {
989 try r.ensureCapacity(a.len);1005 try r.ensureCapacity(a.len);
990 llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);1006 llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
...@@ -1012,6 +1028,8 @@ pub const Int = struct {...@@ -1012,6 +1028,8 @@ pub const Int = struct {
10121028
1013 // r = a & b1029 // r = a & b
1014 pub fn bitAnd(r: *Int, a: Int, b: Int) !void {1030 pub fn bitAnd(r: *Int, a: Int, b: Int) !void {
1031 r.assertWritable();
1032
1015 if (a.len > b.len) {1033 if (a.len > b.len) {
1016 try r.ensureCapacity(b.len);1034 try r.ensureCapacity(b.len);
1017 lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);1035 lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
...@@ -1036,6 +1054,8 @@ pub const Int = struct {...@@ -1036,6 +1054,8 @@ pub const Int = struct {
10361054
1037 // r = a ^ b1055 // r = a ^ b
1038 pub fn bitXor(r: *Int, a: Int, b: Int) !void {1056 pub fn bitXor(r: *Int, a: Int, b: Int) !void {
1057 r.assertWritable();
1058
1039 if (a.len > b.len) {1059 if (a.len > b.len) {
1040 try r.ensureCapacity(a.len);1060 try r.ensureCapacity(a.len);
1041 llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);1061 llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);