Skip to content

Commit 65881f0

Browse files
in-code-i-trustslozier
authored andcommitted
Add support for 'n'/'N' formats in struct (#728)
1 parent e1244f9 commit 65881f0

2 files changed

Lines changed: 108 additions & 0 deletions

File tree

Src/IronPython.Modules/_struct.cs

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ public void __init__(CodeContext/*!*/ context, object fmt) {
154154
WritePointer(res, _isLittleEndian, GetPointer(context, curObj++, values));
155155
}
156156
break;
157+
case FormatType.SignedSizeT:
158+
for (int j = 0; j < curFormat.Count; j++) {
159+
WriteSignedSizeT(res, _isLittleEndian, GetSignedSizeT(context, curObj++, values));
160+
}
161+
break;
162+
case FormatType.SizeT:
163+
for (int j = 0; j < curFormat.Count; j++) {
164+
WriteSizeT(res, _isLittleEndian, GetSizeT(context, curObj++, values));
165+
}
166+
break;
157167
case FormatType.LongLong:
158168
for (int j = 0; j < curFormat.Count; j++) {
159169
WriteLong(res, _isLittleEndian, GetLongValue(context, curObj++, values));
@@ -292,6 +302,24 @@ public void pack_into(CodeContext/*!*/ context, [NotNull]ByteArray/*!*/ buffer,
292302
}
293303
}
294304
break;
305+
case FormatType.SignedSizeT:
306+
for (int j = 0; j < curFormat.Count; j++) {
307+
if (IntPtr.Size == 4) {
308+
res[res_idx++] = CreateIntValue(context, ref curIndex, _isLittleEndian, data);
309+
} else {
310+
res[res_idx++] = BigIntegerOps.__int__(CreateLongValue(context, ref curIndex, _isLittleEndian, data));
311+
}
312+
}
313+
break;
314+
case FormatType.SizeT:
315+
for (int j = 0; j < curFormat.Count; j++) {
316+
if (IntPtr.Size == 4) {
317+
res[res_idx++] = CreateUIntValue(context, ref curIndex, _isLittleEndian, data);
318+
} else {
319+
res[res_idx++] = BigIntegerOps.__int__(CreateULongValue(context, ref curIndex, _isLittleEndian, data));
320+
}
321+
}
322+
break;
295323
case FormatType.LongLong:
296324
for (int j = 0; j < curFormat.Count; j++) {
297325
res[res_idx++] = BigIntegerOps.__int__(CreateLongValue(context, ref curIndex, _isLittleEndian, data));
@@ -450,6 +478,22 @@ private static Struct CompileAndCache(CodeContext/*!*/ context, string/*!*/ fmt)
450478
res.Add(new Format(FormatType.Pointer, count));
451479
count = 1;
452480
break;
481+
case 'n': // intptr_t
482+
if (fStandardized) {
483+
// n and N don't exist in standard sizes
484+
throw Error(context, "bad char in struct format");
485+
}
486+
res.Add(new Format(FormatType.SignedSizeT, count));
487+
count = 1;
488+
break;
489+
case 'N': // uintptr_t
490+
if (fStandardized) {
491+
// n and N don't exist in standard sizes
492+
throw Error(context, "bad char in struct format");
493+
}
494+
res.Add(new Format(FormatType.SizeT, count));
495+
count = 1;
496+
break;
453497
case ' ': // white space, ignore
454498
case '\t':
455499
break;
@@ -567,6 +611,9 @@ private enum FormatType {
567611
CString,
568612
PascalString,
569613
Pointer,
614+
615+
SignedSizeT,
616+
SizeT,
570617
}
571618

572619
private static int GetNativeSize(FormatType c) {
@@ -592,7 +639,10 @@ private static int GetNativeSize(FormatType c) {
592639
case FormatType.Double:
593640
return 8;
594641
case FormatType.Pointer:
642+
case FormatType.SignedSizeT:
595643
return IntPtr.Size;
644+
case FormatType.SizeT:
645+
return UIntPtr.Size;
596646
default:
597647
throw new InvalidOperationException(c.ToString());
598648
}
@@ -747,6 +797,22 @@ private static void WriteUInt(this MemoryStream res, bool fLittleEndian, uint va
747797
}
748798
}
749799

800+
private static void WriteSignedSizeT(this MemoryStream res, bool fLittleEndian, IntPtr val) {
801+
if (IntPtr.Size == 4) {
802+
res.WriteInt(fLittleEndian, val.ToInt32());
803+
} else {
804+
res.WriteLong(fLittleEndian, val.ToInt64());
805+
}
806+
}
807+
808+
private static void WriteSizeT(this MemoryStream res, bool fLittleEndian, UIntPtr val) {
809+
if (IntPtr.Size == 4) {
810+
res.WriteUInt(fLittleEndian, val.ToUInt32());
811+
} else {
812+
res.WriteULong(fLittleEndian, val.ToUInt64());
813+
}
814+
}
815+
750816
private static void WritePointer(this MemoryStream res, bool fLittleEndian, IntPtr val) {
751817
if (IntPtr.Size == 4) {
752818
res.WriteInt(fLittleEndian, val.ToInt32());
@@ -950,6 +1016,34 @@ private static void OutOfRange(CodeContext context, string type) {
9501016
throw Error(context, $"integer out of range for '{(type == "unsigned long" ? "L" : "I")}' format code");
9511017
}
9521018

1019+
internal static IntPtr GetSignedSizeT(CodeContext/*!*/ context, int index, object[] args) {
1020+
object val = GetValue(context, index, args);
1021+
if (IntPtr.Size == 4) {
1022+
if (Converter.TryConvertToInt32(val, out int res)) {
1023+
return new IntPtr(res);
1024+
}
1025+
} else {
1026+
if (Converter.TryConvertToInt64(val, out long res)) {
1027+
return new IntPtr(res);
1028+
}
1029+
}
1030+
throw Error(context, "expected signed size_t(aka ssize_t) value");
1031+
}
1032+
1033+
internal static UIntPtr GetSizeT(CodeContext/*!*/ context, int index, object[] args) {
1034+
object val = GetValue(context, index, args);
1035+
if (IntPtr.Size == 4) {
1036+
if (Converter.TryConvertToUInt32(val, out uint res)) {
1037+
return new UIntPtr(res);
1038+
}
1039+
} else {
1040+
if (Converter.TryConvertToUInt64(val, out ulong res)) {
1041+
return new UIntPtr(res);
1042+
}
1043+
}
1044+
throw Error(context, "expected size_t value");
1045+
}
1046+
9531047
internal static IntPtr GetPointer(CodeContext/*!*/ context, int index, object[] args) {
9541048
object val = GetValue(context, index, args);
9551049
if (IntPtr.Size == 4) {

Tests/test_struct.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,18 @@ def test_ipy2_gh407(self):
6565
struct.unpack("H", b"aa")
6666
self.assertRaisesRegex(struct.error, '^unpack requires', struct.unpack, "H", b"aaa")
6767

68+
def test_nN_code(self):
69+
"""Copied as-is from AllCPython/test_struct.py because that test is currently ignored."""
70+
# n and N don't exist in standard sizes
71+
def assertStructError(func, *args, **kwargs):
72+
with self.assertRaises(struct.error) as cm:
73+
func(*args, **kwargs)
74+
self.assertIn("bad char in struct format", str(cm.exception))
75+
for code in 'nN':
76+
for byteorder in ('=', '<', '>', '!'):
77+
format = byteorder+code
78+
assertStructError(struct.calcsize, format)
79+
assertStructError(struct.pack, format, 0)
80+
assertStructError(struct.unpack, format, b"")
81+
6882
run_test(__name__)

0 commit comments

Comments
 (0)