authorgravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2019-05-04 16:17:12+00:00
committergravatar for tgschultz@gmail.comtgschultz <tgschultz@gmail.com> 2019-05-04 16:17:12+00:00
log8c28b5960559daef3ffc6b0777829ae3262ee8c7
treeef86a52aa007077a25a8789cf4f63f14f4c7a98d
parent27ed525e03879502a983bc4782625e9c081c1c4d

Added ability to specify endianess of PackedInt(Array/Slice)


2 files changed, 335 insertions(+), 161 deletions(-)

std/packed_int_array.zig+333-161
...@@ -3,128 +3,148 @@ const builtin = @import("builtin");...@@ -3,128 +3,148 @@ const builtin = @import("builtin");
3const debug = std.debug;3const debug = std.debug;
4const testing = std.testing;4const testing = std.testing;
55
6pub fn PackedIntIo(comptime Int: type) type {6pub fn PackedIntIo(comptime Int: type, comptime endian: builtin.Endian) type
7{
7 //The general technique employed here is to cast bytes in the array to a container8 //The general technique employed here is to cast bytes in the array to a container
8 // integer (having bits % 8 == 0) large enough to contain the number of bits we want,9 // integer (having bits % 8 == 0) large enough to contain the number of bits we want,
9 // then we can retrieve or store the new value with a relative minimum of masking10 // then we can retrieve or store the new value with a relative minimum of masking
10 // and shifting. In this worst case, this means that we'll need an integer that's11 // and shifting. In this worst case, this means that we'll need an integer that's
11 // actually 1 byte larger than the minimum required to store the bits, because it12 // actually 1 byte larger than the minimum required to store the bits, because it
12 // is possible that the bits start at the end of the first byte, continue through13 // is possible that the bits start at the end of the first byte, continue through
13 // zero or more, then end in the beginning of the last. But, if we try to access14 // zero or more, then end in the beginning of the last. But, if we try to access
14 // a value in the very last byte of memory with that integer size, that extra byte15 // a value in the very last byte of memory with that integer size, that extra byte
15 // will be out of bounds. Depending on the circumstances of the memory, that might16 // will be out of bounds. Depending on the circumstances of the memory, that might
16 // mean the OS fatally kills the program. Thus, we use a larger container (MaxIo)17 // mean the OS fatally kills the program. Thus, we use a larger container (MaxIo)
17 // most of the time, but a smaller container (MinIo) when touching the last byte18 // most of the time, but a smaller container (MinIo) when touching the last byte
18 // of the memory.19 // of the memory.
20
19 const int_bits = comptime std.meta.bitCount(Int);21 const int_bits = comptime std.meta.bitCount(Int);
2022
21 //in the best case, this is the number of bytes we need to touch23 //in the best case, this is the number of bytes we need to touch
22 // to read or write a value, as bits24 // to read or write a value, as bits
23 const min_io_bits = ((int_bits + 7) / 8) * 8;25 const min_io_bits = ((int_bits + 7) / 8) * 8;
2426
25 //in the worst case, this is the number of bytes we need to touch27 //in the worst case, this is the number of bytes we need to touch
26 // to read or write a value, as bits28 // to read or write a value, as bits
27 const max_io_bits = switch (int_bits) {29 const max_io_bits = switch(int_bits)
30 {
28 0 => 0,31 0 => 0,
29 1 => 8,32 1 => 8,
30 2...9 => 16,33 2...9 => 16,
31 10...65535 => ((int_bits / 8) + 2) * 8,34 10...65535 => ((int_bits / 8) + 2) * 8,
32 else => unreachable,35 else => unreachable,
33 };36 };
3437
35 //we bitcast the desired Int type to an unsigned version of itself38 //we bitcast the desired Int type to an unsigned version of itself
36 // to avoid issues with shifting signed ints.39 // to avoid issues with shifting signed ints.
37 const UnInt = @IntType(false, int_bits);40 const UnInt = @IntType(false, int_bits);
3841
39 //The maximum container int type42 //The maximum container int type
40 const MinIo = @IntType(false, min_io_bits);43 const MinIo = @IntType(false, min_io_bits);
4144
42 //The minimum container int type45 //The minimum container int type
43 const MaxIo = @IntType(false, max_io_bits);46 const MaxIo = @IntType(false, max_io_bits);
4447
45 return struct {48 return struct
46 pub fn get(bytes: []const u8, index: usize, bit_offset: u7) Int {49 {
47 if (int_bits == 0) return 0;50 pub fn get(bytes: []const u8, index: usize, bit_offset: u7) Int
4851 {
52 if(int_bits == 0) return 0;
53
49 const bit_index = (index * int_bits) + bit_offset;54 const bit_index = (index * int_bits) + bit_offset;
50 const max_end_byte = (bit_index + max_io_bits) / 8;55 const max_end_byte = (bit_index + max_io_bits) / 8;
5156
52 //Using the larger container size will potentially read out of bounds57 //Using the larger container size will potentially read out of bounds
53 if (max_end_byte > bytes.len) return getBits(bytes, MinIo, bit_index);58 if(max_end_byte > bytes.len) return getBits(bytes, MinIo, bit_index);
54 return getBits(bytes, MaxIo, bit_index);59 return getBits(bytes, MaxIo, bit_index);
55 }60 }
5661
57 fn getBits(bytes: []const u8, comptime Container: type, bit_index: usize) Int {62 fn getBits(bytes: []const u8, comptime Container: type, bit_index: usize) Int
63 {
58 const container_bits = comptime std.meta.bitCount(Container);64 const container_bits = comptime std.meta.bitCount(Container);
59 const Shift = std.math.Log2Int(Container);65 const Shift = std.math.Log2Int(Container);
6066
61 const start_byte = bit_index / 8;67 const start_byte = bit_index / 8;
62 const head_keep_bits = bit_index - (start_byte * 8);68 const head_keep_bits = bit_index - (start_byte * 8);
63 const tail_keep_bits = container_bits - (int_bits + head_keep_bits);69 const tail_keep_bits = container_bits - (int_bits + head_keep_bits);
6470
65 //read bytes as container71 //read bytes as container
66 const value_ptr = @ptrCast(*align(1) const Container, &bytes[start_byte]);72 const value_ptr = @ptrCast(*const align(1) Container, &bytes[start_byte]);
67 var value = value_ptr.*;73 var value = value_ptr.*;
6874
69 switch (builtin.endian) {75 if(endian != builtin.endian) value = @bswap(Container, value);
70 .Big => {76
77 switch(endian)
78 {
79 .Big =>
80 {
71 value <<= @intCast(Shift, head_keep_bits);81 value <<= @intCast(Shift, head_keep_bits);
72 value >>= @intCast(Shift, head_keep_bits);82 value >>= @intCast(Shift, head_keep_bits);
73 value >>= @intCast(Shift, tail_keep_bits);83 value >>= @intCast(Shift, tail_keep_bits);
74 },84 },
75 .Little => {85 .Little =>
86 {
76 value <<= @intCast(Shift, tail_keep_bits);87 value <<= @intCast(Shift, tail_keep_bits);
77 value >>= @intCast(Shift, tail_keep_bits);88 value >>= @intCast(Shift, tail_keep_bits);
78 value >>= @intCast(Shift, head_keep_bits);89 value >>= @intCast(Shift, head_keep_bits);
79 },90 },
80 }91 }
8192
82 return @bitCast(Int, @truncate(UnInt, value));93 return @bitCast(Int, @truncate(UnInt, value));
83 }94 }
8495
85 pub fn set(bytes: []u8, index: usize, bit_offset: u3, int: Int) void {96 pub fn set(bytes: []u8, index: usize, bit_offset: u3, int: Int) void
86 if (int_bits == 0) return;97 {
98 if(int_bits == 0) return;
8799
88 const bit_index = (index * int_bits) + bit_offset;100 const bit_index = (index * int_bits) + bit_offset;
89 const max_end_byte = (bit_index + max_io_bits) / 8;101 const max_end_byte = (bit_index + max_io_bits) / 8;
90102
91 //Using the larger container size will potentially write out of bounds103 //Using the larger container size will potentially write out of bounds
92 if (max_end_byte > bytes.len) return setBits(bytes, MinIo, bit_index, int);104 if(max_end_byte > bytes.len) return setBits(bytes, MinIo, bit_index, int);
93 setBits(bytes, MaxIo, bit_index, int);105 setBits(bytes, MaxIo, bit_index, int);
94 }106 }
95107
96 fn setBits(bytes: []u8, comptime Container: type, bit_index: usize, int: Int) void {108 fn setBits(bytes: []u8, comptime Container: type, bit_index: usize, int: Int) void
109 {
97 const container_bits = comptime std.meta.bitCount(Container);110 const container_bits = comptime std.meta.bitCount(Container);
98 const Shift = std.math.Log2Int(Container);111 const Shift = std.math.Log2Int(Container);
99112
100 const start_byte = bit_index / 8;113 const start_byte = bit_index / 8;
101 const head_keep_bits = bit_index - (start_byte * 8);114 const head_keep_bits = bit_index - (start_byte * 8);
102 const tail_keep_bits = container_bits - (int_bits + head_keep_bits);115 const tail_keep_bits = container_bits - (int_bits + head_keep_bits);
103 const keep_shift = switch (builtin.endian) {116 const keep_shift = switch(endian)
117 {
104 .Big => @intCast(Shift, tail_keep_bits),118 .Big => @intCast(Shift, tail_keep_bits),
105 .Little => @intCast(Shift, head_keep_bits),119 .Little => @intCast(Shift, head_keep_bits),
106 };120 };
107121
108 //position the bits where they need to be in the container122 //position the bits where they need to be in the container
109 const value = @intCast(Container, @bitCast(UnInt, int)) << keep_shift;123 const value = @intCast(Container, @bitCast(UnInt, int)) << keep_shift;
110124
111 //read existing bytes125 //read existing bytes
112 const target_ptr = @ptrCast(*align(1) Container, &bytes[start_byte]);126 const target_ptr = @ptrCast(*align(1) Container, &bytes[start_byte]);
113 var target = target_ptr.*;127 var target = target_ptr.*;
114128
129 if(endian != builtin.endian) target = @bswap(Container, target);
130
115 //zero the bits we want to replace in the existing bytes131 //zero the bits we want to replace in the existing bytes
116 const inv_mask = @intCast(Container, std.math.maxInt(UnInt)) << keep_shift;132 const inv_mask = @intCast(Container, std.math.maxInt(UnInt)) << keep_shift;
117 const mask = ~inv_mask;133 const mask = ~inv_mask;
118 target &= mask;134 target &= mask;
119135
120 //merge the new value136 //merge the new value
121 target |= value;137 target |= value;
122138
139 if(endian != builtin.endian) target = @bswap(Container, target);
140
123 //save it back141 //save it back
124 target_ptr.* = target;142 target_ptr.* = target;
125 }143 }
126144
127 fn slice(bytes: []u8, bit_offset: u3, start: usize, end: usize) PackedIntSlice(Int) {145 fn slice(bytes: []u8, bit_offset: u3, start: usize, end: usize)
146 PackedIntSliceEndian(Int, endian)
147 {
128 debug.assert(end >= start);148 debug.assert(end >= start);
129149
130 const length = end - start;150 const length = end - start;
...@@ -132,25 +152,28 @@ pub fn PackedIntIo(comptime Int: type) type {...@@ -132,25 +152,28 @@ pub fn PackedIntIo(comptime Int: type) type {
132 const start_byte = bit_index / 8;152 const start_byte = bit_index / 8;
133 const end_byte = (bit_index + (length * int_bits) + 7) / 8;153 const end_byte = (bit_index + (length * int_bits) + 7) / 8;
134 const new_bytes = bytes[start_byte..end_byte];154 const new_bytes = bytes[start_byte..end_byte];
135155
136 if (length == 0) return PackedIntSlice(Int).init(new_bytes[0..0], 0);156 if(length == 0) return PackedIntSliceEndian(Int, endian).init(new_bytes[0..0], 0);
137157
138 var new_slice = PackedIntSlice(Int).init(new_bytes, length);158 var new_slice = PackedIntSliceEndian(Int, endian).init(new_bytes, length);
139 new_slice.bit_offset = @intCast(u3, (bit_index - (start_byte * 8)));159 new_slice.bit_offset = @intCast(u3, (bit_index - (start_byte * 8)));
140 return new_slice;160 return new_slice;
141 }161 }
142162
143 fn sliceCast(bytes: []u8, comptime NewInt: type, bit_offset: u3, old_len: usize) PackedIntSlice(NewInt) {163 fn sliceCast(bytes: []u8, comptime NewInt: type, comptime new_endian: builtin.Endian,
164 bit_offset: u3, old_len: usize) PackedIntSliceEndian(NewInt, new_endian)
165 {
144 const new_int_bits = comptime std.meta.bitCount(NewInt);166 const new_int_bits = comptime std.meta.bitCount(NewInt);
145 const New = PackedIntSlice(NewInt);167 const New = PackedIntSliceEndian(NewInt, new_endian);
146168
147 const total_bits = (old_len * int_bits);169 const total_bits = (old_len * int_bits);
148 const new_int_count = total_bits / new_int_bits;170 const new_int_count = total_bits / new_int_bits;
149171
150 debug.assert(total_bits == new_int_count * new_int_bits);172 debug.assert(total_bits == new_int_count * new_int_bits);
151173
152 var new = New.init(bytes, new_int_count);174 var new = New.init(bytes, new_int_count);
153 new.bit_offset = bit_offset;175 new.bit_offset = bit_offset;
176
154 return new;177 return new;
155 }178 }
156 };179 };
...@@ -159,132 +182,187 @@ pub fn PackedIntIo(comptime Int: type) type {...@@ -159,132 +182,187 @@ pub fn PackedIntIo(comptime Int: type) type {
159///Creates a bit-packed array of integers of type Int. Bits182///Creates a bit-packed array of integers of type Int. Bits
160/// are packed using native endianess and without storing any meta183/// are packed using native endianess and without storing any meta
161/// data. PackedIntArray(i3, 8) will occupy exactly 3 bytes of memory.184/// data. PackedIntArray(i3, 8) will occupy exactly 3 bytes of memory.
162pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type {185pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type
186{
187 return PackedIntArrayEndian(Int, builtin.endian, int_count);
188}
189
190///Creates a bit-packed array of integers of type Int. Bits
191/// are packed using specified endianess and without storing any meta
192/// data.
193pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: builtin.Endian,
194 comptime int_count: usize) type
195{
163 const int_bits = comptime std.meta.bitCount(Int);196 const int_bits = comptime std.meta.bitCount(Int);
164 const total_bits = int_bits * int_count;197 const total_bits = int_bits * int_count;
165 const total_bytes = (total_bits + 7) / 8;198 const total_bytes = (total_bits + 7) / 8;
166199
167 const Io = PackedIntIo(Int);200 const Io = PackedIntIo(Int, endian);
168201
169 return struct {202 return struct
203 {
170 const Self = @This();204 const Self = @This();
171205
172 bytes: [total_bytes]u8,206 bytes: [total_bytes]u8,
173207
174 ///Returns the number of elements in the packed array208 ///Returns the number of elements in the packed array
175 pub fn len(self: Self) usize {209 pub fn len(self: Self) usize
210 {
176 return int_count;211 return int_count;
177 }212 }
178213
179 ///Initialize a packed array using an unpacked array214 ///Initialize a packed array using an unpacked array
180 /// or, more likely, an array literal.215 /// or, more likely, an array literal.
181 pub fn init(ints: [int_count]Int) Self {216 pub fn init(ints: [int_count]Int) Self
217 {
182 var self = Self(undefined);218 var self = Self(undefined);
183 for (ints) |int, i| self.set(i, int);219 for(ints) |int, i| self.set(i, int);
184 return self;220 return self;
185 }221 }
186222
187 ///Return the Int stored at index223 ///Return the Int stored at index
188 pub fn get(self: Self, index: usize) Int {224 pub fn get(self: Self, index: usize) Int
225 {
189 debug.assert(index < int_count);226 debug.assert(index < int_count);
190 return Io.get(self.bytes, index, 0);227 return Io.get(self.bytes, index, 0);
191 }228 }
192229
193 ///Copy int into the array at index230 ///Copy int into the array at index
194 pub fn set(self: *Self, index: usize, int: Int) void {231 pub fn set(self: *Self, index: usize, int: Int) void
232 {
195 debug.assert(index < int_count);233 debug.assert(index < int_count);
196 return Io.set(&self.bytes, index, 0, int);234 return Io.set(&self.bytes, index, 0, int);
197 }235 }
198236
199 ///Create a PackedIntSlice of the array from given start to given end237 ///Create a PackedIntSlice of the array from given start to given end
200 pub fn slice(self: *Self, start: usize, end: usize) PackedIntSlice(Int) {238 pub fn slice(self: *Self, start: usize, end: usize) PackedIntSliceEndian(Int, endian)
239 {
201 debug.assert(start < int_count);240 debug.assert(start < int_count);
202 debug.assert(end <= int_count);241 debug.assert(end <= int_count);
203 return Io.slice(&self.bytes, 0, start, end);242 return Io.slice(&self.bytes, 0, start, end);
204 }243 }
205244
206 ///Create a PackedIntSlice of the array using NewInt as the bit width integer.245 ///Create a PackedIntSlice of the array using NewInt as the bit width integer.
207 /// NewInt's bit width must fit evenly within the array's Int's total bits.246 /// NewInt's bit width must fit evenly within the array's Int's total bits.
208 pub fn sliceCast(self: *Self, comptime NewInt: type) PackedIntSlice(NewInt) {247 pub fn sliceCast(self: *Self, comptime NewInt: type) PackedIntSlice(NewInt)
209 return Io.sliceCast(&self.bytes, NewInt, 0, int_count);248 {
249 return self.sliceCastEndian(NewInt, endian);
250 }
251
252 ///Create a PackedIntSlice of the array using NewInt as the bit width integer
253 /// and new_endian as the new endianess. NewInt's bit width must fit evenly within
254 /// the array's Int's total bits.
255 pub fn sliceCastEndian(self: *Self, comptime NewInt: type,
256 comptime new_endian: builtin.Endian) PackedIntSliceEndian(NewInt, new_endian)
257 {
258 return Io.sliceCast(&self.bytes, NewInt, new_endian, 0, int_count);
210 }259 }
211 };260 };
212}261}
213262
214///Uses a slice as a bit-packed block of int_count integers of type Int.263///Uses a slice as a bit-packed block of int_count integers of type Int.
215/// Bits are packed using native endianess and without storing any meta264/// Bits are packed using native endianess and without storing any meta
216/// data.265/// data.
217pub fn PackedIntSlice(comptime Int: type) type {266pub fn PackedIntSlice(comptime Int: type) type
218 const int_bits = comptime std.meta.bitCount(Int);267{
219 const Io = PackedIntIo(Int);268 return PackedIntSliceEndian(Int, builtin.endian);
269}
220270
221 return struct {271///Uses a slice as a bit-packed block of int_count integers of type Int.
272/// Bits are packed using specified endianess and without storing any meta
273/// data.
274pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian) type
275{
276 const int_bits = comptime std.meta.bitCount(Int);
277 const Io = PackedIntIo(Int, endian);
278
279 return struct
280 {
222 const Self = @This();281 const Self = @This();
223282
224 bytes: []u8,283 bytes: []u8,
225 int_count: usize,284 int_count: usize,
226 bit_offset: u3,285 bit_offset: u3,
227286
228 ///Returns the number of elements in the packed slice287 ///Returns the number of elements in the packed slice
229 pub fn len(self: Self) usize {288 pub fn len(self: Self) usize
289 {
230 return self.int_count;290 return self.int_count;
231 }291 }
232292
233 ///Calculates the number of bytes required to store a desired count293 ///Calculates the number of bytes required to store a desired count
234 /// of Ints294 /// of Ints
235 pub fn bytesRequired(int_count: usize) usize {295 pub fn bytesRequired(int_count: usize) usize
296 {
236 const total_bits = int_bits * int_count;297 const total_bits = int_bits * int_count;
237 const total_bytes = (total_bits + 7) / 8;298 const total_bytes = (total_bits + 7) / 8;
238 return total_bytes;299 return total_bytes;
239 }300 }
240301
241 ///Initialize a packed slice using the memory at bytes, with int_count302 ///Initialize a packed slice using the memory at bytes, with int_count
242 /// elements. bytes must be large enough to accomodate the requested303 /// elements. bytes must be large enough to accomodate the requested
243 /// count.304 /// count.
244 pub fn init(bytes: []u8, int_count: usize) Self {305 pub fn init(bytes: []u8, int_count: usize) Self
306 {
245 debug.assert(bytes.len >= bytesRequired(int_count));307 debug.assert(bytes.len >= bytesRequired(int_count));
246308
247 return Self{309 return Self
310 {
248 .bytes = bytes,311 .bytes = bytes,
249 .int_count = int_count,312 .int_count = int_count,
250 .bit_offset = 0,313 .bit_offset = 0,
251 };314 };
252 }315 }
253316
254 ///Return the Int stored at index317 ///Return the Int stored at index
255 pub fn get(self: Self, index: usize) Int {318 pub fn get(self: Self, index: usize) Int
319 {
256 debug.assert(index < self.int_count);320 debug.assert(index < self.int_count);
257 return Io.get(self.bytes, index, self.bit_offset);321 return Io.get(self.bytes, index, self.bit_offset);
258 }322 }
259323
260 ///Copy int into the array at index324 ///Copy int into the array at index
261 pub fn set(self: *Self, index: usize, int: Int) void {325 pub fn set(self: *Self, index: usize, int: Int) void
326 {
262 debug.assert(index < self.int_count);327 debug.assert(index < self.int_count);
263 return Io.set(self.bytes, index, self.bit_offset, int);328 return Io.set(self.bytes, index, self.bit_offset, int);
264 }329 }
265330
266 ///Create a PackedIntSlice of this slice from given start to given end331 ///Create a PackedIntSlice of this slice from given start to given end
267 pub fn slice(self: Self, start: usize, end: usize) PackedIntSlice(Int) {332 pub fn slice(self: Self, start: usize, end: usize) PackedIntSliceEndian(Int, endian)
333 {
268 debug.assert(start < self.int_count);334 debug.assert(start < self.int_count);
269 debug.assert(end <= self.int_count);335 debug.assert(end <= self.int_count);
270 return Io.slice(self.bytes, self.bit_offset, start, end);336 return Io.slice(self.bytes, self.bit_offset, start, end);
271 }337 }
272338
273 ///Create a PackedIntSlice of this slice using NewInt as the bit width integer.339 ///Create a PackedIntSlice of this slice using NewInt as the bit width integer.
274 /// NewInt's bit width must fit evenly within this slice's Int's total bits.340 /// NewInt's bit width must fit evenly within this slice's Int's total bits.
275 pub fn sliceCast(self: Self, comptime NewInt: type) PackedIntSlice(NewInt) {341 pub fn sliceCast(self: Self, comptime NewInt: type) PackedIntSliceEndian(NewInt, endian)
276 return Io.sliceCast(self.bytes, NewInt, self.bit_offset, self.int_count);342 {
343 return self.sliceCastEndian(NewInt, endian);
344 }
345
346 ///Create a PackedIntSlice of this slice using NewInt as the bit width integer
347 /// and new_endian as the new endianess. NewInt's bit width must fit evenly within
348 /// this slice's Int's total bits.
349 pub fn sliceCastEndian(self: Self, comptime NewInt: type,
350 comptime new_endian: builtin.Endian) PackedIntSliceEndian(NewInt, new_endian)
351 {
352 return Io.sliceCast(self.bytes, NewInt, new_endian, self.bit_offset, self.int_count);
277 }353 }
278 };354 };
279}355}
280356
281test "PackedIntArray" {357test "PackedIntArray"
358{
282 @setEvalBranchQuota(10000);359 @setEvalBranchQuota(10000);
283 const max_bits = 256;360 const max_bits = 256;
284 const int_count = 19;361 const int_count = 19;
285362
286 comptime var bits = 0;363 comptime var bits = 0;
287 inline while (bits <= 256) : (bits += 1) {364 inline while(bits <= 256):(bits += 1)
365 {
288 //alternate unsigned and signed366 //alternate unsigned and signed
289 const even = bits % 2 == 0;367 const even = bits % 2 == 0;
290 const I = @IntType(even, bits);368 const I = @IntType(even, bits);
...@@ -292,90 +370,100 @@ test "PackedIntArray" {...@@ -292,90 +370,100 @@ test "PackedIntArray" {
292 const PackedArray = PackedIntArray(I, int_count);370 const PackedArray = PackedIntArray(I, int_count);
293 const expected_bytes = ((bits * int_count) + 7) / 8;371 const expected_bytes = ((bits * int_count) + 7) / 8;
294 testing.expect(@sizeOf(PackedArray) == expected_bytes);372 testing.expect(@sizeOf(PackedArray) == expected_bytes);
295373
296 var data = PackedArray(undefined);374 var data = PackedArray(undefined);
297375
298 //write values, counting up376 //write values, counting up
299 var i = usize(0);377 var i = usize(0);
300 var count = I(0);378 var count = I(0);
301 while (i < data.len()) : (i += 1) {379 while(i < data.len()):(i += 1)
380 {
302 data.set(i, count);381 data.set(i, count);
303 if (bits > 0) count +%= 1;382 if(bits > 0) count +%= 1;
304 }383 }
305384
306 //read and verify values385 //read and verify values
307 i = 0;386 i = 0;
308 count = 0;387 count = 0;
309 while (i < data.len()) : (i += 1) {388 while(i < data.len()):(i += 1)
389 {
310 const val = data.get(i);390 const val = data.get(i);
311 testing.expect(val == count);391 testing.expect(val == count);
312 if (bits > 0) count +%= 1;392 if(bits > 0) count +%= 1;
313 }393 }
314 }394 }
315}395}
316396
317test "PackedIntArray init" {397test "PackedIntArray init"
398{
318 const PackedArray = PackedIntArray(u3, 8);399 const PackedArray = PackedIntArray(u3, 8);
319 var packed_array = PackedArray.init([]u3{ 0, 1, 2, 3, 4, 5, 6, 7 });400 var packed_array = PackedArray.init([]u3{0,1,2,3,4,5,6,7});
320 var i = usize(0);401 var i = usize(0);
321 while (i < packed_array.len()) : (i += 1) testing.expect(packed_array.get(i) == i);402 while(i < packed_array.len()):(i += 1) testing.expect(packed_array.get(i) == i);
322}403}
323404
324test "PackedIntSlice" {405test "PackedIntSlice"
406{
325 @setEvalBranchQuota(10000);407 @setEvalBranchQuota(10000);
326 const max_bits = 256;408 const max_bits = 256;
327 const int_count = 19;409 const int_count = 19;
328 const total_bits = max_bits * int_count;410 const total_bits = max_bits * int_count;
329 const total_bytes = (total_bits + 7) / 8;411 const total_bytes = (total_bits + 7) / 8;
330412
331 var buffer: [total_bytes]u8 = undefined;413 var buffer: [total_bytes]u8 = undefined;
332414
333 comptime var bits = 0;415 comptime var bits = 0;
334 inline while (bits <= 256) : (bits += 1) {416 inline while(bits <= 256):(bits += 1)
417 {
335 //alternate unsigned and signed418 //alternate unsigned and signed
336 const even = bits % 2 == 0;419 const even = bits % 2 == 0;
337 const I = @IntType(even, bits);420 const I = @IntType(even, bits);
338 const P = PackedIntSlice(I);421 const P = PackedIntSlice(I);
339422
340 var data = P.init(&buffer, int_count);423 var data = P.init(&buffer, int_count);
341424
342 //write values, counting up425 //write values, counting up
343 var i = usize(0);426 var i = usize(0);
344 var count = I(0);427 var count = I(0);
345 while (i < data.len()) : (i += 1) {428 while(i < data.len()):(i += 1)
429 {
346 data.set(i, count);430 data.set(i, count);
347 if (bits > 0) count +%= 1;431 if(bits > 0) count +%= 1;
348 }432 }
349433
350 //read and verify values434 //read and verify values
351 i = 0;435 i = 0;
352 count = 0;436 count = 0;
353 while (i < data.len()) : (i += 1) {437 while(i < data.len()):(i += 1)
438 {
354 const val = data.get(i);439 const val = data.get(i);
355 testing.expect(val == count);440 testing.expect(val == count);
356 if (bits > 0) count +%= 1;441 if(bits > 0) count +%= 1;
357 }442 }
358 }443 }
359}444}
360445
361test "PackedIntSlice of PackedInt(Array/Slice)" {446test "PackedIntSlice of PackedInt(Array/Slice)"
447{
362 const max_bits = 16;448 const max_bits = 16;
363 const int_count = 19;449 const int_count = 19;
364450
365 comptime var bits = 0;451 comptime var bits = 0;
366 inline while (bits <= max_bits) : (bits += 1) {452 inline while(bits <= max_bits):(bits += 1)
453 {
367 const Int = @IntType(false, bits);454 const Int = @IntType(false, bits);
368455
369 const PackedArray = PackedIntArray(Int, int_count);456 const PackedArray = PackedIntArray(Int, int_count);
370 var packed_array = PackedArray(undefined);457 var packed_array = PackedArray(undefined);
371458
372 const limit = (1 << bits);459 const limit = (1 << bits);
373460
374 var i = usize(0);461 var i = usize(0);
375 while (i < packed_array.len()) : (i += 1) {462 while(i < packed_array.len()):(i += 1)
463 {
376 packed_array.set(i, @intCast(Int, i % limit));464 packed_array.set(i, @intCast(Int, i % limit));
377 }465 }
378466
379 //slice of array467 //slice of array
380 var packed_slice = packed_array.slice(2, 5);468 var packed_slice = packed_array.slice(2, 5);
381 testing.expect(packed_slice.len() == 3);469 testing.expect(packed_slice.len() == 3);
...@@ -387,10 +475,10 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {...@@ -387,10 +475,10 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {
387 testing.expect(packed_slice.get(2) == 4 % limit);475 testing.expect(packed_slice.get(2) == 4 % limit);
388 packed_slice.set(1, 7 % limit);476 packed_slice.set(1, 7 % limit);
389 testing.expect(packed_slice.get(1) == 7 % limit);477 testing.expect(packed_slice.get(1) == 7 % limit);
390478
391 //write through slice479 //write through slice
392 testing.expect(packed_array.get(3) == 7 % limit);480 testing.expect(packed_array.get(3) == 7 % limit);
393481
394 //slice of a slice482 //slice of a slice
395 const packed_slice_two = packed_slice.slice(0, 3);483 const packed_slice_two = packed_slice.slice(0, 3);
396 testing.expect(packed_slice_two.len() == 3);484 testing.expect(packed_slice_two.len() == 3);
...@@ -399,7 +487,7 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {...@@ -399,7 +487,7 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {
399 testing.expect(packed_slice_two.bytes.len == ps2_expected_bytes);487 testing.expect(packed_slice_two.bytes.len == ps2_expected_bytes);
400 testing.expect(packed_slice_two.get(1) == 7 % limit);488 testing.expect(packed_slice_two.get(1) == 7 % limit);
401 testing.expect(packed_slice_two.get(2) == 4 % limit);489 testing.expect(packed_slice_two.get(2) == 4 % limit);
402490
403 //size one case491 //size one case
404 const packed_slice_three = packed_slice_two.slice(1, 2);492 const packed_slice_three = packed_slice_two.slice(1, 2);
405 testing.expect(packed_slice_three.len() == 1);493 testing.expect(packed_slice_three.len() == 1);
...@@ -407,12 +495,12 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {...@@ -407,12 +495,12 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {
407 const ps3_expected_bytes = (ps3_bit_count + 7) / 8;495 const ps3_expected_bytes = (ps3_bit_count + 7) / 8;
408 testing.expect(packed_slice_three.bytes.len == ps3_expected_bytes);496 testing.expect(packed_slice_three.bytes.len == ps3_expected_bytes);
409 testing.expect(packed_slice_three.get(0) == 7 % limit);497 testing.expect(packed_slice_three.get(0) == 7 % limit);
410498
411 //empty slice case499 //empty slice case
412 const packed_slice_empty = packed_slice.slice(0, 0);500 const packed_slice_empty = packed_slice.slice(0, 0);
413 testing.expect(packed_slice_empty.len() == 0);501 testing.expect(packed_slice_empty.len() == 0);
414 testing.expect(packed_slice_empty.bytes.len == 0);502 testing.expect(packed_slice_empty.bytes.len == 0);
415503
416 //slicing at byte boundaries504 //slicing at byte boundaries
417 const packed_slice_edge = packed_array.slice(8, 16);505 const packed_slice_edge = packed_array.slice(8, 16);
418 testing.expect(packed_slice_edge.len() == 8);506 testing.expect(packed_slice_edge.len() == 8);
...@@ -421,75 +509,154 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {...@@ -421,75 +509,154 @@ test "PackedIntSlice of PackedInt(Array/Slice)" {
421 testing.expect(packed_slice_edge.bytes.len == pse_expected_bytes);509 testing.expect(packed_slice_edge.bytes.len == pse_expected_bytes);
422 testing.expect(packed_slice_edge.bit_offset == 0);510 testing.expect(packed_slice_edge.bit_offset == 0);
423 }511 }
512
424}513}
425514
426test "PackedIntSlice accumulating bit offsets" {515test "PackedIntSlice accumulating bit offsets"
516{
427 //bit_offset is u3, so standard debugging asserts should catch517 //bit_offset is u3, so standard debugging asserts should catch
428 // anything518 // anything
429 {519 {
430 const PackedArray = PackedIntArray(u3, 16);520 const PackedArray = PackedIntArray(u3, 16);
431 var packed_array = PackedArray(undefined);521 var packed_array = PackedArray(undefined);
432522
433 var packed_slice = packed_array.slice(0, packed_array.len());523 var packed_slice = packed_array.slice(0, packed_array.len());
434 var i = usize(0);524 var i = usize(0);
435 while (i < packed_array.len() - 1) : (i += 1) {525 while(i < packed_array.len() - 1):(i += 1)
526 {
527
436 packed_slice = packed_slice.slice(1, packed_slice.len());528 packed_slice = packed_slice.slice(1, packed_slice.len());
437 }529 }
438 }530 }
439 {531 {
440 const PackedArray = PackedIntArray(u11, 88);532 const PackedArray = PackedIntArray(u11, 88);
441 var packed_array = PackedArray(undefined);533 var packed_array = PackedArray(undefined);
442534
443 var packed_slice = packed_array.slice(0, packed_array.len());535 var packed_slice = packed_array.slice(0, packed_array.len());
444 var i = usize(0);536 var i = usize(0);
445 while (i < packed_array.len() - 1) : (i += 1) {537 while(i < packed_array.len() - 1):(i += 1)
538 {
446 packed_slice = packed_slice.slice(1, packed_slice.len());539 packed_slice = packed_slice.slice(1, packed_slice.len());
447 }540 }
448 }541 }
542
449}543}
450544
451//@NOTE: As I do not have a big endian system to test this on,545//@NOTE: As I do not have a big endian system to test this on,
452// big endian values were not tested546// big endian values were not tested
453test "PackedInt(Array/Slice) sliceCast" {547test "PackedInt(Array/Slice) sliceCast"
548{
454 const PackedArray = PackedIntArray(u1, 16);549 const PackedArray = PackedIntArray(u1, 16);
455 var packed_array = PackedArray.init([]u1{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 });550 var packed_array = PackedArray.init([]u1{0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1});
456 const packed_slice_cast_2 = packed_array.sliceCast(u2);551 const packed_slice_cast_2 = packed_array.sliceCast(u2);
457 const packed_slice_cast_4 = packed_slice_cast_2.sliceCast(u4);552 const packed_slice_cast_4 = packed_slice_cast_2.sliceCast(u4);
458 var packed_slice_cast_9 = packed_array.slice(0, (packed_array.len() / 9) * 9).sliceCast(u9);553 var packed_slice_cast_9 = packed_array.slice(0, (packed_array.len() / 9) * 9).sliceCast(u9);
459 const packed_slice_cast_3 = packed_slice_cast_9.sliceCast(u3);554 const packed_slice_cast_3 = packed_slice_cast_9.sliceCast(u3);
460555
461 var i = usize(0);556 var i = usize(0);
462 while (i < packed_slice_cast_2.len()) : (i += 1) {557 while(i < packed_slice_cast_2.len()):(i += 1)
463 const val = switch (builtin.endian) {558 {
559 const val = switch(builtin.endian)
560 {
464 .Big => 0b01,561 .Big => 0b01,
465 .Little => 0b10,562 .Little => 0b10,
466 };563 };
467 testing.expect(packed_slice_cast_2.get(i) == val);564 testing.expect(packed_slice_cast_2.get(i) == val);
468 }565 }
469 i = 0;566 i = 0;
470 while (i < packed_slice_cast_4.len()) : (i += 1) {567 while(i < packed_slice_cast_4.len()):(i += 1)
471 const val = switch (builtin.endian) {568 {
569 const val = switch(builtin.endian)
570 {
472 .Big => 0b0101,571 .Big => 0b0101,
473 .Little => 0b1010,572 .Little => 0b1010,
474 };573 };
475 testing.expect(packed_slice_cast_4.get(i) == val);574 testing.expect(packed_slice_cast_4.get(i) == val);
476 }575 }
477 i = 0;576 i = 0;
478 while (i < packed_slice_cast_9.len()) : (i += 1) {577 while(i < packed_slice_cast_9.len()):(i += 1)
578 {
479 const val = 0b010101010;579 const val = 0b010101010;
480 testing.expect(packed_slice_cast_9.get(i) == val);580 testing.expect(packed_slice_cast_9.get(i) == val);
481 packed_slice_cast_9.set(i, 0b111000111);581 packed_slice_cast_9.set(i, 0b111000111);
482 }582 }
483 i = 0;583 i = 0;
484 while (i < packed_slice_cast_3.len()) : (i += 1) {584 while(i < packed_slice_cast_3.len()):(i += 1)
485 const val = switch (builtin.endian) {585 {
486 .Big => if (i % 2 == 0) u3(0b111) else u3(0b000),586 const val = switch(builtin.endian)
487 .Little => if (i % 2 == 0) u3(0b111) else u3(0b000),587 {
588 .Big => if(i % 2 == 0) u3(0b111) else u3(0b000),
589 .Little => if(i % 2 == 0) u3(0b111) else u3(0b000),
488 };590 };
489 testing.expect(packed_slice_cast_3.get(i) == val);591 testing.expect(packed_slice_cast_3.get(i) == val);
490 }592 }
491}593}
492594
595test "PackedInt(Array/Slice)Endian"
596{
597 {
598 const PackedArrayBe = PackedIntArrayEndian(u4, .Big, 8);
599 var packed_array_be = PackedArrayBe.init([]u4{0,1,2,3,4,5,6,7,});
600 testing.expect(packed_array_be.bytes[0] == 0b00000001);
601 testing.expect(packed_array_be.bytes[1] == 0b00100011);
602
603 var i = usize(0);
604 while(i < packed_array_be.len()):(i += 1)
605 {
606 testing.expect(packed_array_be.get(i) == i);
607 }
608
609 var packed_slice_le = packed_array_be.sliceCastEndian(u4, .Little);
610 i = 0;
611 while(i < packed_slice_le.len()):(i += 1)
612 {
613 const val = if(i % 2 == 0) i + 1 else i - 1;
614 testing.expect(packed_slice_le.get(i) == val);
615 }
616
617 var packed_slice_le_shift = packed_array_be.slice(1, 5).sliceCastEndian(u4, .Little);
618 i = 0;
619 while(i < packed_slice_le_shift.len()):(i += 1)
620 {
621 const val = if(i % 2 == 0) i else i + 2;
622 testing.expect(packed_slice_le_shift.get(i) == val);
623 }
624 }
625
626 {
627 const PackedArrayBe = PackedIntArrayEndian(u11, .Big, 8);
628 var packed_array_be = PackedArrayBe.init([]u11{0,1,2,3,4,5,6,7,});
629 testing.expect(packed_array_be.bytes[0] == 0b00000000);
630 testing.expect(packed_array_be.bytes[1] == 0b00000000);
631 testing.expect(packed_array_be.bytes[2] == 0b00000100);
632 testing.expect(packed_array_be.bytes[3] == 0b00000001);
633 testing.expect(packed_array_be.bytes[4] == 0b00000000);
634
635 var i = usize(0);
636 while(i < packed_array_be.len()):(i += 1)
637 {
638 testing.expect(packed_array_be.get(i) == i);
639 }
640
641 var packed_slice_le = packed_array_be.sliceCastEndian(u11, .Little);
642 testing.expect(packed_slice_le.get(0) == 0b00000000000);
643 testing.expect(packed_slice_le.get(1) == 0b00010000000);
644 testing.expect(packed_slice_le.get(2) == 0b00000000100);
645 testing.expect(packed_slice_le.get(3) == 0b00000000000);
646 testing.expect(packed_slice_le.get(4) == 0b00010000011);
647 testing.expect(packed_slice_le.get(5) == 0b00000000010);
648 testing.expect(packed_slice_le.get(6) == 0b10000010000);
649 testing.expect(packed_slice_le.get(7) == 0b00000111001);
650
651
652 var packed_slice_le_shift = packed_array_be.slice(1, 5).sliceCastEndian(u11, .Little);
653 testing.expect(packed_slice_le_shift.get(0) == 0b00010000000);
654 testing.expect(packed_slice_le_shift.get(1) == 0b00000000100);
655 testing.expect(packed_slice_le_shift.get(2) == 0b00000000000);
656 testing.expect(packed_slice_le_shift.get(3) == 0b00010000011);
657 }
658}
659
493//@NOTE: Need to manually update this list as more posix os's get660//@NOTE: Need to manually update this list as more posix os's get
494// added to DirectAllocator. Windows can be added too when DirectAllocator661// added to DirectAllocator. Windows can be added too when DirectAllocator
495// switches to VirtualAlloc.662// switches to VirtualAlloc.
...@@ -499,39 +666,44 @@ test "PackedInt(Array/Slice) sliceCast" {...@@ -499,39 +666,44 @@ test "PackedInt(Array/Slice) sliceCast" {
499// and reading the last element. The assumption is that the page666// and reading the last element. The assumption is that the page
500// after this one is not mapped and will cause a segfault if we667// after this one is not mapped and will cause a segfault if we
501// don't account for the bounds.668// don't account for the bounds.
502test "PackedIntArray at end of available memory" {669test "PackedIntArray at end of available memory"
503 switch (builtin.os) {670{
671 switch(builtin.os)
672 {
504 .linux, .macosx, .ios, .freebsd, .netbsd => {},673 .linux, .macosx, .ios, .freebsd, .netbsd => {},
505 else => return,674 else => return,
506 }675 }
507 const PackedArray = PackedIntArray(u3, 8);676 const PackedArray = PackedIntArray(u3, 8);
508677
509 const Padded = struct {678 const Padded = struct
679 {
510 _: [std.os.page_size - @sizeOf(PackedArray)]u8,680 _: [std.os.page_size - @sizeOf(PackedArray)]u8,
511 p: PackedArray,681 p: PackedArray,
512 };682 };
513683
514 var da = std.heap.DirectAllocator.init();684 var da = std.heap.DirectAllocator.init();
515 const allocator = &da.allocator;685 const allocator = &da.allocator;
516686
517 var pad = try allocator.create(Padded);687 var pad = try allocator.create(Padded);
518 defer allocator.destroy(pad);688 defer allocator.destroy(pad);
519 pad.p.set(7, std.math.maxInt(u3));689 pad.p.set(7, std.math.maxInt(u3));
520}690}
521691
522test "PackedIntSlice at end of available memory" {692test "PackedIntSlice at end of available memory"
523 switch (builtin.os) {693{
694 switch(builtin.os)
695 {
524 .linux, .macosx, .ios, .freebsd, .netbsd => {},696 .linux, .macosx, .ios, .freebsd, .netbsd => {},
525 else => return,697 else => return,
526 }698 }
527 const PackedSlice = PackedIntSlice(u11);699 const PackedSlice = PackedIntSlice(u11);
528700
529 var da = std.heap.DirectAllocator.init();701 var da = std.heap.DirectAllocator.init();
530 const allocator = &da.allocator;702 const allocator = &da.allocator;
531703
532 var page = try allocator.alloc(u8, std.os.page_size);704 var page = try allocator.alloc(u8, std.os.page_size);
533 defer allocator.free(page);705 defer allocator.free(page);
534706
535 var p = PackedSlice.init(page[std.os.page_size - 2 ..], 1);707 var p = PackedSlice.init(page[std.os.page_size - 2..], 1);
536 p.set(0, std.math.maxInt(u11));708 p.set(0, std.math.maxInt(u11));
537}709}
\ No newline at end of file
std/std.zig+2
...@@ -9,7 +9,9 @@ pub const DynLib = @import("dynamic_library.zig").DynLib;...@@ -9,7 +9,9 @@ pub const DynLib = @import("dynamic_library.zig").DynLib;
9pub const HashMap = @import("hash_map.zig").HashMap;9pub const HashMap = @import("hash_map.zig").HashMap;
10pub const LinkedList = @import("linked_list.zig").LinkedList;10pub const LinkedList = @import("linked_list.zig").LinkedList;
11pub const Mutex = @import("mutex.zig").Mutex;11pub const Mutex = @import("mutex.zig").Mutex;
12pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
12pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;13pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
14pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
13pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;15pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
14pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;16pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
15pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;17pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;