Skip to content

Commit 0b417fc

Browse files
committed
refactor(msgpack): improve code readability and add comprehensive tests
- Add constants for MessagePack type limits and markers - Optimize memory management in map and payload operations - Enhance error handling and type conversion logic - Add comprehensive test suite for edge cases and compatibility - Improve code comments and documentation - Implement deterministic serialization tests
1 parent f9cf587 commit 0b417fc

File tree

2 files changed

+291
-45
lines changed

2 files changed

+291
-45
lines changed

src/msgpack.zig

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,14 @@ const little_endian = switch (current_zig.minor) {
2020
else => @compileError("not support current version zig"),
2121
};
2222

23+
// Constants for improved code readability
24+
const MAX_POSITIVE_FIXINT: u8 = 0x7f;
25+
const MIN_NEGATIVE_FIXINT: i8 = -32;
26+
const MAX_FIXSTR_LEN: u8 = 31;
27+
const MAX_FIXARRAY_LEN: u8 = 15;
28+
const MAX_FIXMAP_LEN: u8 = 15;
29+
const TIMESTAMP_EXT_TYPE: i8 = -1;
30+
2331
/// the Str Type
2432
pub const Str = struct {
2533
str: []const u8,
@@ -158,15 +166,17 @@ pub const Payload = union(enum) {
158166
if (self.* != .map) {
159167
return Errors.NotMap;
160168
}
161-
// TODO: This maybe memory leak
162-
const old_key = self.map.getKey(key);
163-
if (old_key) |old_key_ptr| {
164-
// if the key is already in map, free the old key
165-
self.map.allocator.free(old_key_ptr);
169+
170+
// Check if the key already exists using getKeyPtr
171+
if (self.map.getKeyPtr(key)) |existing_key| {
172+
// Key exists, use the existing allocated key
173+
try self.map.put(existing_key.*, val);
174+
} else {
175+
// Key doesn't exist, create a new key
176+
const new_key = try self.map.allocator.alloc(u8, key.len);
177+
@memcpy(new_key, key);
178+
try self.map.put(new_key, val);
166179
}
167-
const new_key = try self.map.allocator.alloc(u8, key.len);
168-
@memcpy(new_key, key);
169-
try self.map.put(new_key, val);
170180
}
171181

172182
/// get a NIL payload
@@ -206,9 +216,9 @@ pub const Payload = union(enum) {
206216

207217
/// get a str payload
208218
pub fn strToPayload(val: []const u8, allocator: Allocator) !Payload {
209-
// alloca memory
219+
// allocate memory
210220
const new_str = try allocator.alloc(u8, val.len);
211-
// copy the val
221+
// copy the value
212222
@memcpy(new_str, val);
213223
return Payload{
214224
.str = wrapStr(new_str),
@@ -217,9 +227,9 @@ pub const Payload = union(enum) {
217227

218228
/// get a bin payload
219229
pub fn binToPayload(val: []const u8, allocator: Allocator) !Payload {
220-
// alloca memory
230+
// allocate memory
221231
const new_bin = try allocator.alloc(u8, val.len);
222-
// copy the val
232+
// copy the value
223233
@memcpy(new_bin, val);
224234
return Payload{
225235
.bin = wrapBin(new_bin),
@@ -246,9 +256,9 @@ pub const Payload = union(enum) {
246256

247257
/// get an ext payload
248258
pub fn extToPayload(t: i8, data: []const u8, allocator: Allocator) !Payload {
249-
// alloca memory
259+
// allocate memory
250260
const new_data = try allocator.alloc(u8, data.len);
251-
// copy the val
261+
// copy the value
252262
@memcpy(new_data, data);
253263
return Payload{
254264
.ext = wrapEXT(t, new_data),
@@ -269,7 +279,7 @@ pub const Payload = union(enum) {
269279
};
270280
}
271281

272-
/// free the all memeory for this payload and sub payloads
282+
/// free all memory for this payload and sub payloads
273283
/// the allocator is payload's allocator
274284
pub fn free(self: Payload, allocator: Allocator) void {
275285
switch (self) {
@@ -310,31 +320,31 @@ pub const Payload = union(enum) {
310320
}
311321
}
312322

313-
/// get a i64 value from payload
314-
/// Note: if the payload is not a int or the value is too large, it will return MsGPackError.INVALID_TYPE
323+
/// get an i64 value from payload
324+
/// Note: if the payload is not an int or the value is too large, it will return MsGPackError.INVALID_TYPE
315325
pub fn getInt(self: Payload) !i64 {
316326
return switch (self) {
317327
.int => |val| val,
318328
.uint => |val| {
319329
if (val <= std.math.maxInt(i64)) {
320330
return @intCast(val);
321331
}
322-
// TODO: we can not return this error
332+
// Value exceeds i64 range
323333
return MsGPackError.INVALID_TYPE;
324334
},
325335
else => return MsGPackError.INVALID_TYPE,
326336
};
327337
}
328338

329-
/// get a u64 value from payload
339+
/// get an u64 value from payload
330340
/// Note: if the payload is not a uint or the value is negative, it will return MsGPackError.INVALID_TYPE
331341
pub fn getUint(self: Payload) !u64 {
332342
return switch (self) {
333343
.int => |val| {
334344
if (val >= 0) {
335345
return @intCast(val);
336346
}
337-
// TODO: we can not return this error
347+
// Negative values cannot be converted to u64
338348
return MsGPackError.INVALID_TYPE;
339349
},
340350
.uint => |val| val,
@@ -478,7 +488,7 @@ pub fn Pack(
478488

479489
/// write positive fix int
480490
fn writePfixInt(self: Self, val: u8) !void {
481-
if (val <= 0x7f) {
491+
if (val <= MAX_POSITIVE_FIXINT) {
482492
try self.writeByte(val);
483493
} else {
484494
return MsGPackError.INPUT_VALUE_TOO_LARGE;
@@ -536,7 +546,7 @@ pub fn Pack(
536546

537547
/// write negative fix int
538548
fn writeNfixInt(self: Self, val: i8) !void {
539-
if (val >= -32 and val <= -1) {
549+
if (val >= MIN_NEGATIVE_FIXINT and val <= -1) {
540550
try self.writeByte(@bitCast(val));
541551
} else {
542552
return MsGPackError.INPUT_VALUE_TOO_LARGE;
@@ -594,7 +604,7 @@ pub fn Pack(
594604

595605
/// write uint
596606
fn writeUint(self: Self, val: u64) !void {
597-
if (val <= 0x7f) {
607+
if (val <= MAX_POSITIVE_FIXINT) {
598608
try self.writePfixInt(@intCast(val));
599609
} else if (val <= 0xff) {
600610
try self.writeU8(@intCast(val));
@@ -611,7 +621,7 @@ pub fn Pack(
611621
fn writeInt(self: Self, val: i64) !void {
612622
if (val >= 0) {
613623
try self.writeUint(@intCast(val));
614-
} else if (val >= -32) {
624+
} else if (val >= MIN_NEGATIVE_FIXINT) {
615625
try self.writeNfixInt(@intCast(val));
616626
} else if (val >= -128) {
617627
try self.writeI8(@intCast(val));
@@ -672,7 +682,7 @@ pub fn Pack(
672682
/// write fix str
673683
fn writeFixStr(self: Self, str: []const u8) !void {
674684
const len = str.len;
675-
if (len > 0x1f) {
685+
if (len > MAX_FIXSTR_LEN) {
676686
return MsGPackError.STR_DATA_LENGTH_TOO_LONG;
677687
}
678688
const header: u8 = @intFromEnum(Markers.FIXSTR) + @as(u8, @intCast(len));
@@ -738,7 +748,7 @@ pub fn Pack(
738748
/// write str
739749
fn writeStr(self: Self, str: Str) !void {
740750
const len = str.value().len;
741-
if (len <= 0x1f) {
751+
if (len <= MAX_FIXSTR_LEN) {
742752
try self.writeFixStr(str.value());
743753
} else if (len <= 0xff) {
744754
try self.writeStr8(str.value());
@@ -897,13 +907,12 @@ pub fn Pack(
897907
/// write timestamp
898908
fn writeTimestamp(self: Self, timestamp: Timestamp) !void {
899909
// According to MessagePack spec, timestamp uses extension type -1
900-
const TIMESTAMP_TYPE: i8 = -1;
901910

902911
// timestamp 32 format: seconds fit in 32-bit unsigned int and nanoseconds is 0
903912
if (timestamp.nanoseconds == 0 and timestamp.seconds >= 0 and timestamp.seconds <= 0xffffffff) {
904913
var data: [4]u8 = undefined;
905914
std.mem.writeInt(u32, &data, @intCast(timestamp.seconds), big_endian);
906-
const ext = EXT{ .type = TIMESTAMP_TYPE, .data = &data };
915+
const ext = EXT{ .type = TIMESTAMP_EXT_TYPE, .data = &data };
907916
try self.writeExt(ext);
908917
return;
909918
}
@@ -913,7 +922,7 @@ pub fn Pack(
913922
const data64: u64 = (@as(u64, timestamp.nanoseconds) << 34) | @as(u64, @intCast(timestamp.seconds));
914923
var data: [8]u8 = undefined;
915924
std.mem.writeInt(u64, &data, data64, big_endian);
916-
const ext = EXT{ .type = TIMESTAMP_TYPE, .data = &data };
925+
const ext = EXT{ .type = TIMESTAMP_EXT_TYPE, .data = &data };
917926
try self.writeExt(ext);
918927
return;
919928
}
@@ -923,7 +932,7 @@ pub fn Pack(
923932
var data: [12]u8 = undefined;
924933
std.mem.writeInt(u32, data[0..4], timestamp.nanoseconds, big_endian);
925934
std.mem.writeInt(i64, data[4..12], timestamp.seconds, big_endian);
926-
const ext = EXT{ .type = TIMESTAMP_TYPE, .data = &data };
935+
const ext = EXT{ .type = TIMESTAMP_EXT_TYPE, .data = &data };
927936
try self.writeExt(ext);
928937
return;
929938
}
@@ -957,7 +966,7 @@ pub fn Pack(
957966
},
958967
.arr => |arr| {
959968
const len = arr.len;
960-
if (len <= 0xf) {
969+
if (len <= MAX_FIXARRAY_LEN) {
961970
const header: u8 = @intFromEnum(Markers.FIXARRAY) + @as(u8, @intCast(len));
962971
try self.writeU8Value(header);
963972
} else if (len <= 0xffff) {
@@ -975,7 +984,7 @@ pub fn Pack(
975984
},
976985
.map => |map| {
977986
const len = map.count();
978-
if (len <= 0xf) {
987+
if (len <= MAX_FIXMAP_LEN) {
979988
const header: u8 = @intFromEnum(Markers.FIXMAP) + @as(u8, @intCast(len));
980989
try self.writeU8Value(header);
981990
} else if (len <= 0xffff) {
@@ -1373,8 +1382,6 @@ pub fn Pack(
13731382

13741383
/// read ext value or timestamp if it's timestamp type (-1)
13751384
fn readExtValueOrTimestamp(self: Self, marker: Markers, allocator: Allocator) !Payload {
1376-
const TIMESTAMP_TYPE: i8 = -1;
1377-
13781385
// First, check if this could be a timestamp format
13791386
if (marker == .FIXEXT4 or marker == .FIXEXT8 or marker == .EXT8) {
13801387
// Read and check length for EXT8
@@ -1397,7 +1404,7 @@ pub fn Pack(
13971404
// Read the type
13981405
const ext_type = try self.readI8Value();
13991406

1400-
if (ext_type == TIMESTAMP_TYPE) {
1407+
if (ext_type == TIMESTAMP_EXT_TYPE) {
14011408
// This is a timestamp
14021409
if (marker == .FIXEXT4) {
14031410
// timestamp 32
@@ -1430,13 +1437,11 @@ pub fn Pack(
14301437

14311438
/// try to read timestamp from ext data, return error if not timestamp
14321439
fn tryReadTimestamp(self: Self, marker: Markers, _: Allocator) !Timestamp {
1433-
const TIMESTAMP_TYPE: i8 = -1;
1434-
14351440
switch (marker) {
14361441
.FIXEXT4 => {
14371442
// timestamp 32 format
14381443
const ext_type = try self.readI8Value();
1439-
if (ext_type != TIMESTAMP_TYPE) {
1444+
if (ext_type != TIMESTAMP_EXT_TYPE) {
14401445
return MsGPackError.INVALID_TYPE;
14411446
}
14421447
const seconds = try self.readU32Value();
@@ -1445,7 +1450,7 @@ pub fn Pack(
14451450
.FIXEXT8 => {
14461451
// timestamp 64 format
14471452
const ext_type = try self.readI8Value();
1448-
if (ext_type != TIMESTAMP_TYPE) {
1453+
if (ext_type != TIMESTAMP_EXT_TYPE) {
14491454
return MsGPackError.INVALID_TYPE;
14501455
}
14511456
const data64 = try self.readU64Value();
@@ -1460,7 +1465,7 @@ pub fn Pack(
14601465
return MsGPackError.INVALID_TYPE;
14611466
}
14621467
const ext_type = try self.readI8Value();
1463-
if (ext_type != TIMESTAMP_TYPE) {
1468+
if (ext_type != TIMESTAMP_EXT_TYPE) {
14641469
return MsGPackError.INVALID_TYPE;
14651470
}
14661471
const nanoseconds = try self.readU32Value();
@@ -1475,13 +1480,11 @@ pub fn Pack(
14751480

14761481
/// read timestamp from ext data
14771482
fn readTimestamp(self: Self, marker: Markers, _: Allocator) !Timestamp {
1478-
const TIMESTAMP_TYPE: i8 = -1;
1479-
14801483
switch (marker) {
14811484
.FIXEXT4 => {
14821485
// timestamp 32 format
14831486
const ext_type = try self.readI8Value();
1484-
if (ext_type != TIMESTAMP_TYPE) {
1487+
if (ext_type != TIMESTAMP_EXT_TYPE) {
14851488
return MsGPackError.INVALID_TYPE;
14861489
}
14871490
const seconds = try self.readU32Value();
@@ -1490,7 +1493,7 @@ pub fn Pack(
14901493
.FIXEXT8 => {
14911494
// timestamp 64 format
14921495
const ext_type = try self.readI8Value();
1493-
if (ext_type != TIMESTAMP_TYPE) {
1496+
if (ext_type != TIMESTAMP_EXT_TYPE) {
14941497
return MsGPackError.INVALID_TYPE;
14951498
}
14961499
const data64 = try self.readU64Value();
@@ -1505,7 +1508,7 @@ pub fn Pack(
15051508
return MsGPackError.INVALID_TYPE;
15061509
}
15071510
const ext_type = try self.readI8Value();
1508-
if (ext_type != TIMESTAMP_TYPE) {
1511+
if (ext_type != TIMESTAMP_EXT_TYPE) {
15091512
return MsGPackError.INVALID_TYPE;
15101513
}
15111514
const nanoseconds = try self.readU32Value();

0 commit comments

Comments
 (0)