Skip to content

Commit 2ed1e95

Browse files
committed
Merge duplicate implementations of converting bytearrays to integers
1 parent 33da79b commit 2ed1e95

File tree

6 files changed

+103
-117
lines changed

6 files changed

+103
-117
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,8 @@ def test_SignedBigEndian(self):
460460
b'\x80\x00': -32768,
461461
b'\x00\xff\xff': 65535,
462462
b'\xff\x00\x00': -65536,
463-
b'\x80\x00\x00': -8388608
463+
b'\x80\x00\x00': -8388608,
464+
b'\xa0\x00\x00\x00\x00\x00\x00\x00': -6917529027641081856,
464465
}
465466
self.check(tests1, 'big', signed=True)
466467

@@ -487,7 +488,8 @@ def test_SignedLittleEndian(self):
487488
b'\x00\x80': -32768,
488489
b'\xff\xff\x00': 65535,
489490
b'\x00\x00\xff': -65536,
490-
b'\x00\x00\x80': -8388608
491+
b'\x00\x00\x80': -8388608,
492+
b'\x00\x00\x00\x00\x00\x00\x00\xa0': -6917529027641081856,
491493
}
492494
self.check(tests2, 'little', signed=True)
493495

@@ -505,6 +507,7 @@ def test_UnsignedBigEndian(self):
505507
b'\x80\x00': 32768,
506508
b'\xff\xff': 65535,
507509
b'\x01\x00\x00': 65536,
510+
b'\xa0\x00\x00\x00\x00\x00\x00\x00': 11529215046068469760,
508511
}
509512
self.check(tests3, 'big', signed=False)
510513

@@ -522,6 +525,7 @@ def test_UnsignedLittleEndian(self):
522525
b'\x00\x80': 32768,
523526
b'\xff\xff': 65535,
524527
b'\x00\x00\x01': 65536,
528+
b'\x00\x00\x00\x00\x00\x00\x00\xa0': 11529215046068469760,
525529
}
526530
self.check(tests4, 'little', signed=False)
527531

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/PTextIO.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -47,13 +47,13 @@
4747
import static com.oracle.graal.python.util.PythonUtils.TS_ENCODING;
4848

4949
import java.io.ByteArrayOutputStream;
50-
import java.math.BigInteger;
5150

5251
import com.oracle.graal.python.builtins.objects.ints.IntBuiltins;
52+
import com.oracle.graal.python.builtins.objects.ints.IntNodes;
5353
import com.oracle.graal.python.builtins.objects.ints.PInt;
5454
import com.oracle.graal.python.nodes.PRaiseNode;
55-
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
5655
import com.oracle.truffle.api.CompilerDirectives;
56+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
5757
import com.oracle.truffle.api.memory.ByteArraySupport;
5858
import com.oracle.truffle.api.nodes.Node;
5959
import com.oracle.truffle.api.object.Shape;
@@ -413,15 +413,15 @@ protected static class CookieType {
413413
this.needEOF = 0;
414414
}
415415

416-
public static PInt build(CookieType cookie, PythonObjectFactory factory) {
416+
@TruffleBoundary
417+
public static Object build(CookieType cookie) {
417418
byte[] buffer = new byte[COOKIE_BUF_LEN];
418419
SERIALIZE.putLong(buffer, 0, cookie.startPos);
419420
SERIALIZE.putInt(buffer, Long.BYTES, cookie.decFlags);
420421
SERIALIZE.putInt(buffer, Long.BYTES + Integer.BYTES, cookie.bytesToFeed);
421422
SERIALIZE.putInt(buffer, Long.BYTES + Integer.BYTES * 2, cookie.charsToSkip);
422423
SERIALIZE.putByte(buffer, Long.BYTES + Integer.BYTES * 3, cookie.needEOF);
423-
BigInteger v = IntBuiltins.FromBytesNode.createBigInteger(buffer, false, false);
424-
return factory.createInt(v);
424+
return IntNodes.PyLongFromByteArray.executeUncached(buffer, false, false);
425425
}
426426

427427
public static CookieType parse(long v, Node inliningTarget, InlinedConditionProfile overflow, PRaiseNode.Lazy raise) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/io/TextIOWrapperBuiltins.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -876,11 +876,10 @@ static Object didntMove(VirtualFrame frame, PTextIO self,
876876
@Exclusive @Cached TextIOWrapperNodes.WriteFlushNode writeFlushNode,
877877
@Exclusive @Cached PyObjectCallMethodObjArgs callMethodFlush,
878878
@Exclusive @Cached PyObjectCallMethodObjArgs callMethodTell,
879-
@Exclusive @Cached PyLongAsLongNode asLongNode,
880-
@Shared @Cached PythonObjectFactory factory) {
879+
@Exclusive @Cached PyLongAsLongNode asLongNode) {
881880
PTextIO.CookieType cookie = getCookie(frame, inliningTarget, self, writeFlushNode, callMethodFlush, callMethodTell, asLongNode);
882881
/* We haven't moved from the snapshot point. */
883-
return PTextIO.CookieType.build(cookie, factory);
882+
return PTextIO.CookieType.build(cookie);
884883
}
885884

886885
@Specialization(guards = {
@@ -907,7 +906,7 @@ static Object tell(VirtualFrame frame, PTextIO self,
907906
@Exclusive @Cached PyLongAsLongNode asLongNode,
908907
@Cached PyObjectSizeNode sizeNode,
909908
@CachedLibrary(limit = "2") InteropLibrary isString,
910-
@Shared @Cached PythonObjectFactory factory,
909+
@Cached PythonObjectFactory factory,
911910
@Cached PRaiseNode.Lazy raiseNode) {
912911
PTextIO.CookieType cookie = getCookie(frame, inliningTarget, self, writeFlushNode, callMethodFlush, callMethodTell, asLongNode);
913912
byte[] snapshotNextInput = self.getSnapshotNextInput();
@@ -957,7 +956,7 @@ static Object tell(VirtualFrame frame, PTextIO self,
957956

958957
/* The returned cookie corresponds to the last safe start point. */
959958
cookie.charsToSkip = decodedCharsUsed;
960-
return PTextIO.CookieType.build(cookie, factory);
959+
return PTextIO.CookieType.build(cookie);
961960
}
962961

963962
int charsDecoded = 0;
@@ -1007,7 +1006,7 @@ static Object tell(VirtualFrame frame, PTextIO self,
10071006

10081007
/* The returned cookie corresponds to the last safe start point. */
10091008
cookie.charsToSkip = decodedCharsUsed;
1010-
return PTextIO.CookieType.build(cookie, factory);
1009+
return PTextIO.CookieType.build(cookie);
10111010
}
10121011

10131012
static void fail(VirtualFrame frame, Node inliningTarget, PTextIO self, Object savedState,

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntBuiltins.java

Lines changed: 23 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,6 @@
121121
import com.oracle.graal.python.builtins.objects.common.FormatNodeBase;
122122
import com.oracle.graal.python.builtins.objects.ints.IntBuiltinsClinicProviders.FormatNodeClinicProviderGen;
123123
import com.oracle.graal.python.builtins.objects.tuple.PTuple;
124-
import com.oracle.graal.python.builtins.objects.type.PythonBuiltinClass;
125124
import com.oracle.graal.python.builtins.objects.type.TpSlots;
126125
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotBinaryOp.BinaryOpBuiltinNode;
127126
import com.oracle.graal.python.builtins.objects.type.slots.TpSlotInquiry.NbBoolBuiltinNode;
@@ -131,8 +130,8 @@
131130
import com.oracle.graal.python.nodes.ErrorMessages;
132131
import com.oracle.graal.python.nodes.PRaiseNode;
133132
import com.oracle.graal.python.nodes.SpecialMethodNames;
133+
import com.oracle.graal.python.nodes.call.CallNode;
134134
import com.oracle.graal.python.nodes.call.special.LookupAndCallUnaryNode;
135-
import com.oracle.graal.python.nodes.call.special.LookupAndCallVarargsNode;
136135
import com.oracle.graal.python.nodes.classes.IsSubtypeNode;
137136
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
138137
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
@@ -141,6 +140,7 @@
141140
import com.oracle.graal.python.nodes.function.builtins.PythonTernaryBuiltinNode;
142141
import com.oracle.graal.python.nodes.function.builtins.PythonUnaryBuiltinNode;
143142
import com.oracle.graal.python.nodes.function.builtins.clinic.ArgumentClinicProvider;
143+
import com.oracle.graal.python.nodes.object.BuiltinClassProfiles;
144144
import com.oracle.graal.python.nodes.object.GetClassNode.GetPythonObjectClassNode;
145145
import com.oracle.graal.python.nodes.truffle.PythonArithmeticTypes;
146146
import com.oracle.graal.python.runtime.PythonContext;
@@ -2829,85 +2829,43 @@ protected ArgumentClinicProvider getArgumentClinic() {
28292829
@GenerateNodeFactory
28302830
public abstract static class FromBytesNode extends PythonClinicBuiltinNode {
28312831

2832-
@Child private LookupAndCallVarargsNode constructNode;
2833-
2834-
private static byte[] littleToBig(byte[] bytes) {
2835-
// PInt uses Java BigInteger which are big-endian
2836-
byte[] bigEndianBytes = new byte[bytes.length];
2837-
for (int i = 0; i < bytes.length; i++) {
2838-
bigEndianBytes[bytes.length - i - 1] = bytes[i];
2839-
}
2840-
return bigEndianBytes;
2841-
}
2842-
2843-
@TruffleBoundary
2844-
public static BigInteger createBigInteger(byte[] bytes, boolean isBigEndian, boolean signed) {
2845-
if (bytes.length == 0) {
2846-
// in case of empty byte array
2847-
return BigInteger.ZERO;
2848-
}
2849-
BigInteger result;
2850-
if (isBigEndian) { // big byteorder
2851-
result = signed ? new BigInteger(bytes) : new BigInteger(1, bytes);
2852-
} else { // little byteorder
2853-
byte[] converted = littleToBig(bytes);
2854-
result = signed ? new BigInteger(converted) : new BigInteger(1, converted);
2855-
}
2856-
return result;
2857-
}
2858-
2859-
@TruffleBoundary
2860-
private static boolean isBigEndian(Node raisingNode, TruffleString order) {
2861-
if (order.equalsUncached(T_BIG, TS_ENCODING)) {
2862-
return true;
2863-
}
2864-
if (order.equalsUncached(T_LITTLE, TS_ENCODING)) {
2865-
return false;
2866-
}
2867-
throw PRaiseNode.raiseUncached(raisingNode, PythonErrorType.ValueError, ErrorMessages.BYTEORDER_MUST_BE_LITTLE_OR_BIG);
2868-
}
2869-
2870-
private Object createIntObject(Object cl, BigInteger number, PythonObjectFactory factory) {
2871-
PythonBuiltinClassType type = null;
2872-
if (cl instanceof PythonBuiltinClass) {
2873-
type = ((PythonBuiltinClass) cl).getType();
2874-
} else if (cl instanceof PythonBuiltinClassType) {
2875-
type = (PythonBuiltinClassType) cl;
2876-
}
2877-
if (type == PythonBuiltinClassType.PInt) {
2878-
return factory.createInt(number);
2879-
}
2880-
if (constructNode == null) {
2881-
CompilerDirectives.transferToInterpreterAndInvalidate();
2882-
constructNode = insert(LookupAndCallVarargsNode.create(SpecialMethodNames.T___CALL__));
2883-
}
2884-
return constructNode.execute(null, cl, new Object[]{cl, factory.createInt(number)});
2885-
}
2886-
2887-
private Object compute(Object cl, byte[] bytes, TruffleString byteorder, boolean signed, PythonObjectFactory factory) {
2888-
BigInteger bi = createBigInteger(bytes, isBigEndian(this, byteorder), signed);
2889-
return createIntObject(cl, bi, factory);
2890-
}
2891-
28922832
@Specialization
2893-
Object fromObject(VirtualFrame frame, Object cl, Object object, TruffleString byteorder, boolean signed,
2833+
static Object fromObject(VirtualFrame frame, Object cl, Object object, TruffleString byteorder, boolean signed,
28942834
@Bind("this") Node inliningTarget,
28952835
@Cached("create(Bytes)") LookupAndCallUnaryNode callBytes,
28962836
@CachedLibrary(limit = "1") PythonBufferAccessLibrary bufferLib,
2837+
@Cached BuiltinClassProfiles.IsBuiltinClassExactProfile isBuiltinIntProfile,
2838+
@Cached InlinedBranchProfile hasBytesProfile,
2839+
@Cached TruffleString.EqualNode equalNode,
28972840
@Cached BytesNodes.BytesFromObject bytesFromObject,
2898-
@Cached PythonObjectFactory factory,
2841+
@Cached IntNodes.PyLongFromByteArray fromByteArray,
2842+
@Cached CallNode callCtor,
28992843
@Cached PRaiseNode.Lazy raiseNode) {
2844+
boolean bigEndian;
2845+
if (equalNode.execute(byteorder, T_BIG, TS_ENCODING)) {
2846+
bigEndian = true;
2847+
} else if (equalNode.execute(byteorder, T_LITTLE, TS_ENCODING)) {
2848+
bigEndian = false;
2849+
} else {
2850+
throw raiseNode.get(inliningTarget).raise(PythonErrorType.ValueError, ErrorMessages.BYTEORDER_MUST_BE_LITTLE_OR_BIG);
2851+
}
29002852
byte[] bytes;
29012853
Object bytesObj = callBytes.executeObject(frame, object);
29022854
if (bytesObj != PNone.NO_VALUE) {
2855+
hasBytesProfile.enter(inliningTarget);
29032856
if (!(bytesObj instanceof PBytes)) {
29042857
throw raiseNode.get(inliningTarget).raise(TypeError, ErrorMessages.RETURNED_NONBYTES, T___BYTES__);
29052858
}
29062859
bytes = bufferLib.getCopiedByteArray(bytesObj);
29072860
} else {
29082861
bytes = bytesFromObject.execute(frame, object);
29092862
}
2910-
return compute(cl, bytes, byteorder, signed, factory);
2863+
Object result = fromByteArray.execute(inliningTarget, bytes, bigEndian, signed);
2864+
if (isBuiltinIntProfile.profileClass(inliningTarget, cl, PythonBuiltinClassType.PInt)) {
2865+
return result;
2866+
} else {
2867+
return callCtor.execute(frame, cl, result);
2868+
}
29112869
}
29122870

29132871
@Override

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/ints/IntNodes.java

Lines changed: 47 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,12 @@
4040
*/
4141
package com.oracle.graal.python.builtins.objects.ints;
4242

43+
import static com.oracle.graal.python.builtins.PythonBuiltinClassType.OverflowError;
4344
import static com.oracle.graal.python.nodes.ErrorMessages.TOO_LARGE_TO_CONVERT;
4445

4546
import java.math.BigInteger;
4647

47-
import com.oracle.graal.python.builtins.PythonBuiltinClassType;
48+
import com.oracle.graal.python.nodes.ErrorMessages;
4849
import com.oracle.graal.python.nodes.PRaiseNode;
4950
import com.oracle.graal.python.runtime.object.PythonObjectFactory;
5051
import com.oracle.graal.python.util.NumericSupport;
@@ -55,6 +56,7 @@
5556
import com.oracle.truffle.api.dsl.GenerateUncached;
5657
import com.oracle.truffle.api.dsl.Specialization;
5758
import com.oracle.truffle.api.nodes.Node;
59+
import com.oracle.truffle.api.profiles.InlinedBranchProfile;
5860

5961
/**
6062
* Namespace containing equivalent nodes of {@code _Pylong_XXX} private function from
@@ -153,7 +155,7 @@ static byte[] doArbitraryBytesLong(Node inliningTarget, long value, int size, bo
153155
try {
154156
support.putBigInteger(bytes, 0, PInt.longToBigInteger(value), size);
155157
} catch (OverflowException oe) {
156-
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.OverflowError, TOO_LARGE_TO_CONVERT, "int");
158+
throw raiseNode.get(inliningTarget).raise(OverflowError, TOO_LARGE_TO_CONVERT, "int");
157159
}
158160
return bytes;
159161
}
@@ -166,7 +168,7 @@ static byte[] doPInt(Node inliningTarget, PInt value, int size, boolean bigEndia
166168
try {
167169
support.putBigInteger(bytes, 0, value.getValue(), size);
168170
} catch (OverflowException oe) {
169-
throw raiseNode.get(inliningTarget).raise(PythonBuiltinClassType.OverflowError, TOO_LARGE_TO_CONVERT, "int");
171+
throw raiseNode.get(inliningTarget).raise(OverflowError, TOO_LARGE_TO_CONVERT, "int");
170172
}
171173
return bytes;
172174
}
@@ -176,36 +178,59 @@ static byte[] doPInt(Node inliningTarget, PInt value, int size, boolean bigEndia
176178
* Equivalent to CPython's {@code _PyLong_FromByteArray}.
177179
*/
178180
@GenerateInline(inlineByDefault = true)
181+
@GenerateUncached
179182
public abstract static class PyLongFromByteArray extends Node {
180183
public abstract Object execute(Node inliningTarget, byte[] data, boolean bigEndian, boolean signed);
181184

182185
public final Object executeCached(byte[] data, boolean bigEndian, boolean signed) {
183186
return execute(this, data, bigEndian, signed);
184187
}
185188

186-
protected static int asWellSizedData(int len) {
187-
return switch (len) {
188-
case 1, 2, 4, 8 -> len;
189-
default -> -1;
190-
};
191-
}
192-
193-
@Specialization(guards = {"!signed", "data.length == cachedDataLen"}, limit = "4")
194-
static long doLong(byte[] data, boolean bigEndian, @SuppressWarnings("unused") boolean signed,
195-
@Cached("asWellSizedData(data.length)") int cachedDataLen) {
196-
NumericSupport support = bigEndian ? NumericSupport.bigEndian() : NumericSupport.littleEndian();
197-
return support.getLong(data, 0, cachedDataLen);
189+
public static Object executeUncached(byte[] data, boolean bigEndian, boolean signed) {
190+
return IntNodesFactory.PyLongFromByteArrayNodeGen.getUncached().execute(null, data, bigEndian, signed);
198191
}
199192

200193
@Specialization
201-
static Object doOther(byte[] data, boolean bigEndian, boolean signed,
202-
@Cached(inline = false) PythonObjectFactory factory) {
194+
static Object doOther(Node inliningTarget, byte[] data, boolean bigEndian, boolean signed,
195+
@Cached InlinedBranchProfile fastPath1,
196+
@Cached InlinedBranchProfile fastPath2,
197+
@Cached InlinedBranchProfile fastPath4,
198+
@Cached InlinedBranchProfile fastPath8,
199+
@Cached InlinedBranchProfile generic,
200+
@Cached(inline = false) PythonObjectFactory factory,
201+
@Cached PRaiseNode.Lazy raiseNode) {
203202
NumericSupport support = bigEndian ? NumericSupport.bigEndian() : NumericSupport.littleEndian();
204-
BigInteger integer = support.getBigInteger(data, signed);
205-
if (PInt.bigIntegerFitsInLong(integer)) {
206-
return PInt.longValue(integer);
207-
} else {
208-
return factory.createInt(support.getBigInteger(data, signed));
203+
if (signed) {
204+
switch (data.length) {
205+
case 1 -> {
206+
fastPath1.enter(inliningTarget);
207+
return (int) support.getByte(data, 0);
208+
}
209+
case 2 -> {
210+
fastPath2.enter(inliningTarget);
211+
return (int) support.getShort(data, 0);
212+
}
213+
case 4 -> {
214+
fastPath4.enter(inliningTarget);
215+
return support.getInt(data, 0);
216+
}
217+
case 8 -> {
218+
fastPath8.enter(inliningTarget);
219+
return support.getLong(data, 0);
220+
}
221+
}
222+
}
223+
generic.enter(inliningTarget);
224+
try {
225+
BigInteger integer = support.getBigInteger(data, signed);
226+
if (PInt.bigIntegerFitsInLong(integer)) {
227+
long longValue = PInt.longValue(integer);
228+
return PInt.isIntRange(longValue) ? (int) longValue : longValue;
229+
} else {
230+
return factory.createInt(integer);
231+
}
232+
} catch (OverflowException e) {
233+
throw raiseNode.get(inliningTarget).raise(OverflowError, ErrorMessages.BYTE_ARRAY_TOO_LONG_TO_CONVERT_TO_INT);
209234
}
210235
}
211236
}

0 commit comments

Comments
 (0)