Skip to content

Commit 1c05c9a

Browse files
committed
[GR-12457] Implementation of int.from_bytes has to be corrected.
PullRequest: graalpython/267
2 parents b37ca66 + 05d0c6d commit 1c05c9a

File tree

5 files changed

+573
-34
lines changed

5 files changed

+573
-34
lines changed

graalpython/com.oracle.graal.python.test/src/tests/test_int.py

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@
3737
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3838
# SOFTWARE.
3939

40+
import unittest
41+
import sys
42+
43+
import array
44+
4045
class _NamedIntConstant(int):
4146
def __new__(cls, value, name):
4247
self = super(_NamedIntConstant, cls).__new__(cls, value)
@@ -315,3 +320,244 @@ def __int__(self):
315320

316321
def test_create_int_from_string():
317322
assert int("5c7920a80f5261a2e5322163c79b71a25a41f414", 16) == 527928385865769069253929759180846776123316630548
323+
324+
325+
class FromBytesTests(unittest.TestCase):
326+
327+
def check(self, tests, byteorder, signed=False):
328+
for test, expected in tests.items():
329+
try:
330+
self.assertEqual( int.from_bytes(test, byteorder, signed=signed), expected)
331+
except Exception as err:
332+
raise AssertionError(
333+
"failed to convert {0} with byteorder={1!r} and signed={2}"
334+
.format(test, byteorder, signed)) from err
335+
336+
def test_SignedBigEndian(self):
337+
# Convert signed big-endian byte arrays to integers.
338+
tests1 = {
339+
b'': 0,
340+
b'\x00': 0,
341+
b'\x00\x00': 0,
342+
b'\x01': 1,
343+
b'\x00\x01': 1,
344+
b'\xff': -1,
345+
b'\xff\xff': -1,
346+
b'\x81': -127,
347+
b'\x80': -128,
348+
b'\xff\x7f': -129,
349+
b'\x7f': 127,
350+
b'\x00\x81': 129,
351+
b'\xff\x01': -255,
352+
b'\xff\x00': -256,
353+
b'\x00\xff': 255,
354+
b'\x01\x00': 256,
355+
b'\x7f\xff': 32767,
356+
b'\x80\x00': -32768,
357+
b'\x00\xff\xff': 65535,
358+
b'\xff\x00\x00': -65536,
359+
b'\x80\x00\x00': -8388608
360+
}
361+
self.check(tests1, 'big', signed=True)
362+
363+
def test_SignedLittleEndian(self):
364+
# Convert signed little-endian byte arrays to integers.
365+
tests2 = {
366+
b'': 0,
367+
b'\x00': 0,
368+
b'\x00\x00': 0,
369+
b'\x01': 1,
370+
b'\x00\x01': 256,
371+
b'\xff': -1,
372+
b'\xff\xff': -1,
373+
b'\x81': -127,
374+
b'\x80': -128,
375+
b'\x7f\xff': -129,
376+
b'\x7f': 127,
377+
b'\x81\x00': 129,
378+
b'\x01\xff': -255,
379+
b'\x00\xff': -256,
380+
b'\xff\x00': 255,
381+
b'\x00\x01': 256,
382+
b'\xff\x7f': 32767,
383+
b'\x00\x80': -32768,
384+
b'\xff\xff\x00': 65535,
385+
b'\x00\x00\xff': -65536,
386+
b'\x00\x00\x80': -8388608
387+
}
388+
self.check(tests2, 'little', signed=True)
389+
390+
def test_UnsignedBigEndian(self):
391+
# Convert unsigned big-endian byte arrays to integers.
392+
tests3 = {
393+
b'': 0,
394+
b'\x00': 0,
395+
b'\x01': 1,
396+
b'\x7f': 127,
397+
b'\x80': 128,
398+
b'\xff': 255,
399+
b'\x01\x00': 256,
400+
b'\x7f\xff': 32767,
401+
b'\x80\x00': 32768,
402+
b'\xff\xff': 65535,
403+
b'\x01\x00\x00': 65536,
404+
}
405+
self.check(tests3, 'big', signed=False)
406+
407+
def test_UnsignedLittleEndian(self):
408+
# Convert integers to unsigned little-endian byte arrays.
409+
tests4 = {
410+
b'': 0,
411+
b'\x00': 0,
412+
b'\x01': 1,
413+
b'\x7f': 127,
414+
b'\x80': 128,
415+
b'\xff': 255,
416+
b'\x00\x01': 256,
417+
b'\xff\x7f': 32767,
418+
b'\x00\x80': 32768,
419+
b'\xff\xff': 65535,
420+
b'\x00\x00\x01': 65536,
421+
}
422+
self.check(tests4, 'little', signed=False)
423+
424+
def test_IntObject(self):
425+
myint = MyInt
426+
self.assertIs(type(myint.from_bytes(b'\x00', 'big')), MyInt)
427+
self.assertEqual(myint.from_bytes(b'\x01', 'big'), 1)
428+
self.assertIs(
429+
type(myint.from_bytes(b'\x00', 'big', signed=False)), myint)
430+
self.assertEqual(myint.from_bytes(b'\x01', 'big', signed=False), 1)
431+
self.assertIs(type(myint.from_bytes(b'\x00', 'little')), myint)
432+
self.assertEqual(myint.from_bytes(b'\x01', 'little'), 1)
433+
self.assertIs(type(myint.from_bytes(
434+
b'\x00', 'little', signed=False)), myint)
435+
self.assertEqual(myint.from_bytes(b'\x01', 'little', signed=False), 1)
436+
437+
def test_from_list(self):
438+
self.assertEqual(
439+
int.from_bytes([255, 0, 0], 'big', signed=True), -65536)
440+
self.assertEqual(
441+
MyInt.from_bytes([255, 0, 0], 'big', signed=True), -65536)
442+
self.assertIs(type(MyInt.from_bytes(
443+
[255, 0, 0], 'little', signed=False)), MyInt)
444+
445+
class LyingList(list):
446+
def __iter__(self):
447+
return iter([10, 20, 30, 40])
448+
449+
self.assertEqual(
450+
int.from_bytes(LyingList([255, 1, 1]), 'big'), 169090600)
451+
452+
def test_from_tuple(self):
453+
self.assertEqual(
454+
int.from_bytes((255, 0, 0), 'big', signed=True), -65536)
455+
self.assertEqual(
456+
MyInt.from_bytes((255, 0, 0), 'big', signed=True), -65536)
457+
self.assertIs(type(MyInt.from_bytes(
458+
(255, 0, 0), 'little', signed=False)), MyInt)
459+
460+
class LyingTuple(tuple):
461+
def __iter__(self):
462+
return iter((15, 25, 35, 45))
463+
self.assertEqual(
464+
int.from_bytes(LyingTuple((255, 1, 1)), 'big'), 253305645)
465+
466+
def test_from_bytearray(self):
467+
self.assertEqual(int.from_bytes(
468+
bytearray(b'\xff\x00\x00'), 'big', signed=True), -65536)
469+
self.assertEqual(int.from_bytes(
470+
bytearray(b'\xff\x00\x00'), 'big', signed=True), -65536)
471+
472+
def test_from_array(self):
473+
self.assertEqual(int.from_bytes(
474+
array.array('b', b'\xff\x00\x00'), 'big', signed=True), -65536)
475+
476+
'''
477+
TODO This test is commented out until GR-12448 is not fixed.
478+
def test_from_memoryview(self):
479+
self.assertEqual(int.from_bytes(
480+
memoryview(b'\xff\x00\x00'), 'big', signed=True), -65536)
481+
'''
482+
483+
def test_wrong_input(self):
484+
self.assertRaises(ValueError, int.from_bytes, [256], 'big')
485+
self.assertRaises(ValueError, int.from_bytes, (256,), 'big')
486+
self.assertRaises(ValueError, int.from_bytes, [0], 'big\x00')
487+
self.assertRaises(ValueError, int.from_bytes, [0], 'little\x00')
488+
self.assertRaises(TypeError, int.from_bytes, 0, 'big')
489+
self.assertRaises(TypeError, int.from_bytes, 0, 'big', True)
490+
491+
#TODO uncoment these tests, when GR-12453 is fixed
492+
#self.assertRaises(TypeError, int.from_bytes, "", 'big')
493+
#self.assertRaises(TypeError, int.from_bytes, "\x00", 'big')
494+
#self.assertRaises(TypeError, MyInt.from_bytes, "", 'big')
495+
#self.assertRaises(TypeError, MyInt.from_bytes, "\x00", 'big')
496+
self.assertRaises(TypeError, MyInt.from_bytes, 0, 'big')
497+
self.assertRaises(TypeError, int.from_bytes, 0, 'big', True)
498+
499+
def test_int_subclass(self):
500+
class myint2(int):
501+
def __new__(cls, value):
502+
return int.__new__(cls, value + 1)
503+
504+
i = myint2.from_bytes(b'\x01', 'big')
505+
self.assertIs(type(i), myint2)
506+
if (sys.version_info.major >= 3 and sys.version_info.minor >= 6):
507+
# It doesn't pass on old CPython
508+
self.assertEqual(i, 2)
509+
510+
class myint3(int):
511+
def __init__(self, value):
512+
self.foo = 'bar'
513+
514+
i = myint3.from_bytes(b'\x01', 'big')
515+
self.assertIs(type(i), myint3)
516+
self.assertEqual(i, 1)
517+
if (sys.version_info.major >= 3 and sys.version_info.minor >= 6):
518+
# It doesn't pass on old CPython
519+
self.assertEqual(getattr(i, 'foo', 'none'), 'bar')
520+
521+
def test_range(self):
522+
self.assertEqual(int.from_bytes(range(5), 'big'), 16909060)
523+
self.assertEqual(int.from_bytes(range(5), 'little'), 17230332160)
524+
self.assertEqual(int.from_bytes(range(200,225), 'big'), 1260368276743602661175172759269383066378083427695751132536800)
525+
r = range(10)
526+
self.assertEqual(int.from_bytes(r[:], 'big'), 18591708106338011145)
527+
self.assertEqual(int.from_bytes(r[1:3], 'big'), 258)
528+
self.assertEqual(int.from_bytes(r[3:1], 'big'), 0)
529+
self.assertEqual(int.from_bytes(r[3:-1], 'big'), 3315799033608)
530+
531+
def test_map(self):
532+
def myconvert(text):
533+
return int(text)
534+
self.assertEqual(int.from_bytes(map(myconvert, ["100","10","1"]), 'big'), 6556161)
535+
536+
def test_from_byteslike_object(self):
537+
class mybyteslike():
538+
def __bytes__(self):
539+
return bytes([10,20])
540+
541+
self.assertEqual(int.from_bytes(mybyteslike(), 'big'), 2580)
542+
543+
def test_from_wrong_byteslike_object(self):
544+
class mybyteslike1():
545+
def __bytes__(self):
546+
return range(3)
547+
548+
self.assertRaises(TypeError, int.from_bytes, mybyteslike1(), 'big')
549+
550+
class mybyteslike2():
551+
def __bytes__(self):
552+
return array.array('b', [2, 2, 3])
553+
554+
self.assertRaises(TypeError, int.from_bytes, mybyteslike2(), 'big')
555+
556+
def test_from_list_with_byteslike(self):
557+
class StrangeList(list):
558+
def __bytes__(self):
559+
return bytes([3])
560+
def __iter__(self):
561+
return iter([10])
562+
563+
self.assertEqual(int.from_bytes(StrangeList([4,5]), 'big'), 3)

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ private static final String[] initializeCoreFiles() {
190190
"_warnings",
191191
"posix",
192192
"_io",
193+
"int",
193194
"_frozen_importlib",
194195
"classes",
195196
"_weakref",

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/bytes/BytesNodes.java

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
import com.oracle.graal.python.builtins.objects.bytes.BytesNodesFactory.ToBytesNodeGen;
5050
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes;
5151
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.NormalizeIndexNode;
52-
import com.oracle.graal.python.builtins.objects.list.PList;
5352
import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
5453
import com.oracle.graal.python.nodes.PGuards;
5554
import com.oracle.graal.python.nodes.PNodeWithContext;
@@ -59,13 +58,16 @@
5958
import com.oracle.graal.python.nodes.control.GetNextNode;
6059
import com.oracle.graal.python.nodes.object.IsBuiltinClassProfile;
6160
import com.oracle.graal.python.runtime.exception.PException;
61+
import com.oracle.graal.python.runtime.sequence.PSequence;
62+
import com.oracle.graal.python.runtime.sequence.storage.ByteSequenceStorage;
6263
import com.oracle.graal.python.runtime.sequence.storage.SequenceStorage;
6364
import com.oracle.truffle.api.CompilerDirectives;
6465
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
6566
import com.oracle.truffle.api.dsl.Cached;
6667
import com.oracle.truffle.api.dsl.Fallback;
6768
import com.oracle.truffle.api.dsl.ImportStatic;
6869
import com.oracle.truffle.api.dsl.Specialization;
70+
import com.oracle.truffle.api.nodes.Node;
6971
import com.oracle.truffle.api.profiles.ValueProfile;
7072

7173
public abstract class BytesNodes {
@@ -281,18 +283,17 @@ public static FindNode create() {
281283
}
282284
}
283285

284-
public abstract static class FromListNode extends PNodeWithContext {
286+
public static class FromSequenceStorageNode extends Node {
285287

286-
@Child private SequenceStorageNodes.GetItemNode getItemNode;
287-
@Child private SequenceStorageNodes.CastToByteNode castToByteNode;
288-
@Child private SequenceStorageNodes.LenNode lenNode;
288+
@Node.Child private SequenceStorageNodes.GetItemNode getItemNode;
289+
@Node.Child private SequenceStorageNodes.CastToByteNode castToByteNode;
290+
@Node.Child private SequenceStorageNodes.LenNode lenNode;
289291

290-
public byte[] execute(PList list) {
291-
SequenceStorage listStore = list.getSequenceStorage();
292-
int len = getLenNode().execute(listStore);
292+
public byte[] execute(SequenceStorage storage) {
293+
int len = getLenNode().execute(storage);
293294
byte[] bytes = new byte[len];
294295
for (int i = 0; i < len; i++) {
295-
Object item = getGetItemNode().execute(listStore, i);
296+
Object item = getGetItemNode().execute(storage, i);
296297
bytes[i] = getCastToByteNode().execute(item);
297298
}
298299
return bytes;
@@ -321,6 +322,62 @@ private SequenceStorageNodes.LenNode getLenNode() {
321322
}
322323
return lenNode;
323324
}
325+
326+
public static FromSequenceStorageNode create() {
327+
return new FromSequenceStorageNode();
328+
}
329+
}
330+
331+
public static class FromSequenceNode extends Node {
332+
333+
@Child private FromSequenceStorageNode fromSequenceStorageNode;
334+
335+
public byte[] execute(PSequence sequence) {
336+
if (fromSequenceStorageNode == null) {
337+
CompilerDirectives.transferToInterpreterAndInvalidate();
338+
fromSequenceStorageNode = insert(FromSequenceStorageNode.create());
339+
}
340+
341+
return fromSequenceStorageNode.execute(sequence.getSequenceStorage());
342+
}
343+
344+
public static FromSequenceNode create() {
345+
return new FromSequenceNode();
346+
}
347+
}
348+
349+
public abstract static class FromIteratorNode extends PNodeWithContext {
350+
351+
@Child private SequenceStorageNodes.AppendNode appendByteNode;
352+
353+
public abstract byte[] execute(Object iterator);
354+
355+
public SequenceStorageNodes.AppendNode getAppendByteNode() {
356+
if (appendByteNode == null) {
357+
CompilerDirectives.transferToInterpreterAndInvalidate();
358+
appendByteNode = insert(SequenceStorageNodes.AppendNode.create(() -> SequenceStorageNodes.NoGeneralizationNode.create("byte must be in range(0, 256)")));
359+
}
360+
return appendByteNode;
361+
}
362+
363+
@Specialization
364+
public byte[] doIt(Object iterObject,
365+
@Cached("create()") GetNextNode getNextNode,
366+
@Cached("create()") IsBuiltinClassProfile errorProfile) {
367+
ByteSequenceStorage bss = new ByteSequenceStorage(16);
368+
while (true) {
369+
try {
370+
getAppendByteNode().execute(bss, getNextNode.execute(iterObject));
371+
} catch (PException e) {
372+
e.expectStopIteration(errorProfile);
373+
return bss.getInternalByteArray();
374+
}
375+
}
376+
}
377+
378+
public static FromIteratorNode create() {
379+
return BytesNodesFactory.FromIteratorNodeGen.create();
380+
}
324381
}
325382

326383
public static class CmpNode extends PNodeWithContext {

0 commit comments

Comments
 (0)