Skip to content

Commit 3086e59

Browse files
committed
[GR-12457] Implementation of int.from_bytes has to be corrected.
1 parent 7097f9b commit 3086e59

File tree

5 files changed

+605
-33
lines changed

5 files changed

+605
-33
lines changed

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

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

40+
import unittest
41+
42+
import array
43+
4044
class _NamedIntConstant(int):
4145
def __new__(cls, value, name):
4246
self = super(_NamedIntConstant, cls).__new__(cls, value)
@@ -315,3 +319,241 @@ def __int__(self):
315319

316320
def test_create_int_from_string():
317321
assert int("5c7920a80f5261a2e5322163c79b71a25a41f414", 16) == 527928385865769069253929759180846776123316630548
322+
323+
324+
class FromBytesTests(unittest.TestCase):
325+
326+
def check(self, tests, byteorder, signed=False):
327+
for test, expected in tests.items():
328+
try:
329+
self.assertEqual( int.from_bytes(test, byteorder, signed=signed), expected)
330+
except Exception as err:
331+
raise AssertionError(
332+
"failed to convert {0} with byteorder={1!r} and signed={2}"
333+
.format(test, byteorder, signed)) from err
334+
335+
def test_SignedBigEndian(self):
336+
# Convert signed big-endian byte arrays to integers.
337+
tests1 = {
338+
b'': 0,
339+
b'\x00': 0,
340+
b'\x00\x00': 0,
341+
b'\x01': 1,
342+
b'\x00\x01': 1,
343+
b'\xff': -1,
344+
b'\xff\xff': -1,
345+
b'\x81': -127,
346+
b'\x80': -128,
347+
b'\xff\x7f': -129,
348+
b'\x7f': 127,
349+
b'\x00\x81': 129,
350+
b'\xff\x01': -255,
351+
b'\xff\x00': -256,
352+
b'\x00\xff': 255,
353+
b'\x01\x00': 256,
354+
b'\x7f\xff': 32767,
355+
b'\x80\x00': -32768,
356+
b'\x00\xff\xff': 65535,
357+
b'\xff\x00\x00': -65536,
358+
b'\x80\x00\x00': -8388608
359+
}
360+
self.check(tests1, 'big', signed=True)
361+
362+
def test_SignedLittleEndian(self):
363+
# Convert signed little-endian byte arrays to integers.
364+
tests2 = {
365+
b'': 0,
366+
b'\x00': 0,
367+
b'\x00\x00': 0,
368+
b'\x01': 1,
369+
b'\x00\x01': 256,
370+
b'\xff': -1,
371+
b'\xff\xff': -1,
372+
b'\x81': -127,
373+
b'\x80': -128,
374+
b'\x7f\xff': -129,
375+
b'\x7f': 127,
376+
b'\x81\x00': 129,
377+
b'\x01\xff': -255,
378+
b'\x00\xff': -256,
379+
b'\xff\x00': 255,
380+
b'\x00\x01': 256,
381+
b'\xff\x7f': 32767,
382+
b'\x00\x80': -32768,
383+
b'\xff\xff\x00': 65535,
384+
b'\x00\x00\xff': -65536,
385+
b'\x00\x00\x80': -8388608
386+
}
387+
self.check(tests2, 'little', signed=True)
388+
389+
def test_UnsignedBigEndian(self):
390+
# Convert unsigned big-endian byte arrays to integers.
391+
tests3 = {
392+
b'': 0,
393+
b'\x00': 0,
394+
b'\x01': 1,
395+
b'\x7f': 127,
396+
b'\x80': 128,
397+
b'\xff': 255,
398+
b'\x01\x00': 256,
399+
b'\x7f\xff': 32767,
400+
b'\x80\x00': 32768,
401+
b'\xff\xff': 65535,
402+
b'\x01\x00\x00': 65536,
403+
}
404+
self.check(tests3, 'big', signed=False)
405+
406+
def test_UnsignedLittleEndian(self):
407+
# Convert integers to unsigned little-endian byte arrays.
408+
tests4 = {
409+
b'': 0,
410+
b'\x00': 0,
411+
b'\x01': 1,
412+
b'\x7f': 127,
413+
b'\x80': 128,
414+
b'\xff': 255,
415+
b'\x00\x01': 256,
416+
b'\xff\x7f': 32767,
417+
b'\x00\x80': 32768,
418+
b'\xff\xff': 65535,
419+
b'\x00\x00\x01': 65536,
420+
}
421+
self.check(tests4, 'little', signed=False)
422+
423+
def test_IntObject(self):
424+
myint = MyInt
425+
self.assertIs(type(myint.from_bytes(b'\x00', 'big')), MyInt)
426+
self.assertEqual(myint.from_bytes(b'\x01', 'big'), 1)
427+
self.assertIs(
428+
type(myint.from_bytes(b'\x00', 'big', signed=False)), myint)
429+
self.assertEqual(myint.from_bytes(b'\x01', 'big', signed=False), 1)
430+
self.assertIs(type(myint.from_bytes(b'\x00', 'little')), myint)
431+
self.assertEqual(myint.from_bytes(b'\x01', 'little'), 1)
432+
self.assertIs(type(myint.from_bytes(
433+
b'\x00', 'little', signed=False)), myint)
434+
self.assertEqual(myint.from_bytes(b'\x01', 'little', signed=False), 1)
435+
436+
def test_from_list(self):
437+
self.assertEqual(
438+
int.from_bytes([255, 0, 0], 'big', signed=True), -65536)
439+
self.assertEqual(
440+
MyInt.from_bytes([255, 0, 0], 'big', signed=True), -65536)
441+
self.assertIs(type(MyInt.from_bytes(
442+
[255, 0, 0], 'little', signed=False)), MyInt)
443+
444+
class LyingList(list):
445+
def __iter__(self):
446+
return iter([10, 20, 30, 40])
447+
448+
self.assertEqual(
449+
int.from_bytes(LyingList([255, 1, 1]), 'big'), 169090600)
450+
451+
def test_from_tuple(self):
452+
self.assertEqual(
453+
int.from_bytes((255, 0, 0), 'big', signed=True), -65536)
454+
self.assertEqual(
455+
MyInt.from_bytes((255, 0, 0), 'big', signed=True), -65536)
456+
self.assertIs(type(MyInt.from_bytes(
457+
(255, 0, 0), 'little', signed=False)), MyInt)
458+
459+
class LyingTuple(tuple):
460+
def __iter__(self):
461+
return iter((15, 25, 35, 45))
462+
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+
self.assertEqual(i, 2)
507+
508+
class myint3(int):
509+
def __init__(self, value):
510+
self.foo = 'bar'
511+
512+
i = myint3.from_bytes(b'\x01', 'big')
513+
self.assertIs(type(i), myint3)
514+
self.assertEqual(i, 1)
515+
self.assertEqual(getattr(i, 'foo', 'none'), 'bar')
516+
517+
def test_range(self):
518+
self.assertEqual(int.from_bytes(range(5), 'big'), 16909060)
519+
self.assertEqual(int.from_bytes(range(5), 'little'), 17230332160)
520+
self.assertEqual(int.from_bytes(range(200,225), 'big'), 1260368276743602661175172759269383066378083427695751132536800)
521+
r = range(10)
522+
self.assertEqual(int.from_bytes(r[:], 'big'), 18591708106338011145)
523+
self.assertEqual(int.from_bytes(r[1:3], 'big'), 258)
524+
self.assertEqual(int.from_bytes(r[3:1], 'big'), 0)
525+
self.assertEqual(int.from_bytes(r[3:-1], 'big'), 3315799033608)
526+
527+
def test_map(self):
528+
def myconvert(text):
529+
return int(text)
530+
self.assertEqual(int.from_bytes(map(myconvert, ["100","10","1"]), 'big'), 6556161)
531+
532+
def test_from_byteslike_object(self):
533+
class mybyteslike():
534+
def __bytes__(self):
535+
return bytes([10,20])
536+
537+
self.assertEqual(int.from_bytes(mybyteslike(), 'big'), 2580)
538+
539+
def test_from_wrong_byteslike_object(self):
540+
class mybyteslike1():
541+
def __bytes__(self):
542+
return range(3)
543+
544+
self.assertRaises(TypeError, int.from_bytes, mybyteslike1(), 'big')
545+
546+
class mybyteslike2():
547+
def __bytes__(self):
548+
return array.array('b', [2, 2, 3])
549+
550+
self.assertRaises(TypeError, int.from_bytes, mybyteslike2(), 'big')
551+
552+
def test_from_list_with_byteslike(self):
553+
class StrangeList(list):
554+
def __bytes__(self):
555+
return bytes([3])
556+
def __iter__(self):
557+
return iter([10])
558+
559+
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: 93 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import com.oracle.graal.python.builtins.objects.common.SequenceStorageNodes.NormalizeIndexNode;
5252
import com.oracle.graal.python.builtins.objects.list.PList;
5353
import com.oracle.graal.python.builtins.objects.memoryview.PMemoryView;
54+
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
5455
import com.oracle.graal.python.nodes.PGuards;
5556
import com.oracle.graal.python.nodes.PNodeWithContext;
5657
import com.oracle.graal.python.nodes.SpecialMethodNames;
@@ -66,6 +67,7 @@
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,20 @@ public static FindNode create() {
281283
}
282284
}
283285

284-
public abstract static class FromListNode extends PNodeWithContext {
286+
public abstract static class FromSequenceStorageNode extends PNodeWithContext {
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 abstract byte[] execute(SequenceStorage storage);
293+
294+
@Specialization
295+
public byte[] doIt(SequenceStorage storage) {
296+
int len = getLenNode().execute(storage);
293297
byte[] bytes = new byte[len];
294298
for (int i = 0; i < len; i++) {
295-
Object item = getGetItemNode().execute(listStore, i);
299+
Object item = getGetItemNode().execute(storage, i);
296300
bytes[i] = getCastToByteNode().execute(item);
297301
}
298302
return bytes;
@@ -321,6 +325,87 @@ private SequenceStorageNodes.LenNode getLenNode() {
321325
}
322326
return lenNode;
323327
}
328+
329+
public static FromSequenceStorageNode createNode() {
330+
return BytesNodesFactory.FromSequenceStorageNodeGen.create();
331+
}
332+
}
333+
334+
public abstract static class FromListNode extends FromSequenceStorageNode {
335+
336+
public abstract byte[] execute(PList iterator);
337+
338+
@Specialization
339+
public byte[] doIt(PList list) {
340+
return doIt(list.getSequenceStorage());
341+
}
342+
343+
public static FromListNode create() {
344+
return BytesNodesFactory.FromListNodeGen.create();
345+
}
346+
}
347+
348+
public abstract static class FromTupleNode extends FromSequenceStorageNode {
349+
350+
public abstract byte[] execute(PTuple tuple);
351+
352+
@Specialization
353+
public byte[] doIt(PTuple tuple) {
354+
return doIt(tuple.getSequenceStorage());
355+
}
356+
357+
public static FromTupleNode create() {
358+
return BytesNodesFactory.FromTupleNodeGen.create();
359+
}
360+
}
361+
362+
public abstract static class FromIteratorNode extends PNodeWithContext {
363+
364+
public abstract byte[] execute(Object iterator);
365+
366+
@Specialization
367+
public byte[] execute(Object iterObject,
368+
@Cached("create()") SequenceStorageNodes.CastToByteNode castToByteNode,
369+
@Cached("create()") GetNextNode getNextNode,
370+
@Cached("create()") IsBuiltinClassProfile errorProfile) {
371+
ArrayList<byte[]> parts = new ArrayList<>();
372+
int currentLenght = 16;
373+
int currentOffset = 0;
374+
byte[] currentArray = new byte[currentLenght];
375+
parts.add(currentArray);
376+
int size = 0;
377+
while (true) {
378+
if (currentOffset >= currentArray.length) {
379+
// we need to create new array
380+
if (currentLenght < 65536) {
381+
currentLenght = currentLenght * 2;
382+
}
383+
currentArray = new byte[currentLenght];
384+
parts.add(currentArray);
385+
currentOffset = 0;
386+
}
387+
try {
388+
currentArray[currentOffset] = castToByteNode.execute(getNextNode.execute(iterObject));
389+
currentOffset++;
390+
size++;
391+
} catch (PException e) {
392+
e.expectStopIteration(errorProfile);
393+
// convert parts into result array
394+
byte[] bytes = new byte[size];
395+
396+
int offset = 0;
397+
for (byte[] part : parts) {
398+
System.arraycopy(part, 0, bytes, offset, Math.min(part.length, size - offset));
399+
offset += part.length;
400+
}
401+
return bytes;
402+
}
403+
}
404+
}
405+
406+
public static FromIteratorNode create() {
407+
return BytesNodesFactory.FromIteratorNodeGen.create();
408+
}
324409
}
325410

326411
public static class CmpNode extends PNodeWithContext {

0 commit comments

Comments
 (0)