Skip to content

Commit c88ed96

Browse files
committed
[GR-13553] Implement bytes.translate().
PullRequest: graalpython/382
2 parents 6ad022a + feb72a2 commit c88ed96

File tree

4 files changed

+251
-3
lines changed

4 files changed

+251
-3
lines changed

graalpython/com.oracle.graal.python.test/src/graalpytest.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
22
# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
33
#
44
# The Universal Permissive License (UPL), Version 1.0
@@ -140,6 +140,11 @@ def assertIs(self, actual, expected, msg=""):
140140
msg = "Expected '%r' to be '%r'" % (actual, expected)
141141
assert actual is expected, msg
142142

143+
def assertIsNot(self, actual, expected, msg=""):
144+
if not msg:
145+
msg = "Expected '%r' not to be '%r'" % (actual, expected)
146+
assert actual is not expected, msg
147+
143148
def assertEqual(self, expected, actual, msg=None):
144149
if not msg:
145150
msg = "Expected '%r' to be equal to '%r'" % (actual, expected)

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
# SOFTWARE.
3939

4040
import unittest
41+
import sys
4142

4243
def assert_raises(err, fn, *args, **kwargs):
4344
raised = False
@@ -640,8 +641,59 @@ def test_maketrans(self):
640641
self.assertRaises(ValueError, self.type2test.maketrans, b'abc', b'xyzq')
641642
self.assertRaises(TypeError, self.type2test.maketrans, 'abc', 'def')
642643

644+
def test_translate(self):
645+
b = self.type2test(b'hello')
646+
rosetta = bytearray(range(256))
647+
rosetta[ord('o')] = ord('e')
648+
649+
self.assertRaises(TypeError, b.translate)
650+
self.assertRaises(TypeError, b.translate, None, None)
651+
self.assertRaises(ValueError, b.translate, bytes(range(255)))
652+
653+
c = b.translate(rosetta, b'hello')
654+
self.assertEqual(b, b'hello')
655+
self.assertIsInstance(c, self.type2test)
656+
657+
c = b.translate(rosetta)
658+
d = b.translate(rosetta, b'')
659+
self.assertEqual(c, d)
660+
self.assertEqual(c, b'helle')
661+
662+
c = b.translate(rosetta, b'l')
663+
self.assertEqual(c, b'hee')
664+
665+
c = b.translate(None, b'e')
666+
self.assertEqual(c, b'hllo')
667+
668+
if (sys.version_info.major >= 3 and sys.version_info.minor >= 6):
669+
# test delete as a keyword argument
670+
c = b.translate(rosetta, delete=b'')
671+
self.assertEqual(c, b'helle')
672+
c = b.translate(rosetta, delete=b'l')
673+
self.assertEqual(c, b'hee')
674+
c = b.translate(None, delete=b'e')
675+
self.assertEqual(c, b'hllo')
676+
643677
class BytesTest(BaseLikeBytes, unittest.TestCase):
644678
type2test = bytes
645679

680+
def test_translate_no_change(self):
681+
b = b'ahoj'
682+
self.assertIs(b, b.translate(None))
683+
self.assertIs(b, b.translate(None, b''))
684+
table = bytearray(range(256))
685+
self.assertIs(b, b.translate(table))
686+
self.assertIs(b, b.translate(table), b'')
687+
self.assertIs(b, b.translate(table), b'klp')
688+
646689
class ByteArrayTest(BaseLikeBytes, unittest.TestCase):
647690
type2test = bytearray
691+
692+
def test_translate_no_change(self):
693+
b = bytearray(b'ahoj')
694+
self.assertIsNot(b, b.translate(None))
695+
self.assertIsNot(b, b.translate(None, b''))
696+
table = bytearray(range(256))
697+
self.assertIsNot(b, b.translate(table))
698+
self.assertIsNot(b, b.translate(table), b'')
699+
self.assertIsNot(b, b.translate(table), b'klp')

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/MarshalModuleBuiltins.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2018, Oracle and/or its affiliates.
2+
* Copyright (c) 2017, 2019, Oracle and/or its affiliates.
33
* Copyright (c) 2013, Regents of the University of California
44
*
55
* All rights reserved.
@@ -34,6 +34,7 @@
3434
import com.oracle.graal.python.builtins.PythonBuiltins;
3535
import com.oracle.graal.python.nodes.function.PythonBuiltinBaseNode;
3636
import com.oracle.graal.python.nodes.function.PythonBuiltinNode;
37+
import com.oracle.truffle.api.CompilerDirectives;
3738
import com.oracle.truffle.api.dsl.GenerateNodeFactory;
3839
import com.oracle.truffle.api.dsl.NodeFactory;
3940
import com.oracle.truffle.api.dsl.Specialization;
@@ -59,9 +60,11 @@ Object doit(Object value, Object file, Object version) {
5960
@GenerateNodeFactory
6061
abstract static class DumpsNode extends PythonBuiltinNode {
6162
@SuppressWarnings("unused")
63+
@CompilerDirectives.TruffleBoundary
6264
@Specialization
6365
Object doit(Object value, Object version) {
64-
throw raise(NotImplementedError, "marshal.dumps");
66+
// TODO this is just fake implemention, which satisfy pip installer
67+
return factory().createBytes(value.toString().getBytes());
6568
}
6669
}
6770

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

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -806,4 +806,192 @@ public PBytes maketrans(@SuppressWarnings("unused") PythonClass cls, Object from
806806
}
807807

808808
}
809+
810+
// bytes.translate(table, delete=b'')
811+
// bytearray.translate(table, delete=b'')
812+
@Builtin(name = "translate", minNumOfPositionalArgs = 2, maxNumOfPositionalArgs = 3, keywordArguments = {"delete"})
813+
@GenerateNodeFactory
814+
public abstract static class TranslateNode extends PythonBuiltinNode {
815+
816+
@Child BytesNodes.ToBytesNode toBytesNode;
817+
818+
@CompilationFinal private ConditionProfile isLenTable256Profile;
819+
820+
private BytesNodes.ToBytesNode getToBytesNode() {
821+
if (toBytesNode == null) {
822+
CompilerDirectives.transferToInterpreterAndInvalidate();
823+
toBytesNode = insert(BytesNodes.ToBytesNode.create());
824+
}
825+
return toBytesNode;
826+
}
827+
828+
private void checkLengthOfTable(byte[] table) {
829+
if (isLenTable256Profile == null) {
830+
CompilerDirectives.transferToInterpreterAndInvalidate();
831+
isLenTable256Profile = ConditionProfile.createBinaryProfile();
832+
}
833+
834+
if (isLenTable256Profile.profile(table.length != 256)) {
835+
throw raise(PythonErrorType.ValueError, "translation table must be 256 characters long");
836+
}
837+
}
838+
839+
private static class Result {
840+
byte[] array;
841+
// we have to know, whether the result array was changed ->
842+
// if not in bytes case it has to return the input bytes
843+
// in bytearray case it has to return always new bytearray
844+
boolean changed;
845+
846+
public Result(byte[] array, boolean changed) {
847+
this.array = array;
848+
this.changed = changed;
849+
}
850+
}
851+
852+
private static boolean[] createDeleteTable(byte[] delete) {
853+
boolean[] result = new boolean[256];
854+
for (int i = 0; i < 256; i++) {
855+
result[i] = false;
856+
}
857+
for (int i = 0; i < delete.length; i++) {
858+
result[delete[i]] = true;
859+
}
860+
return result;
861+
}
862+
863+
private static Result delete(byte[] self, byte[] table) {
864+
final int length = self.length;
865+
byte[] result = new byte[length];
866+
int resultLen = 0;
867+
boolean[] toDelete = createDeleteTable(table);
868+
869+
for (int i = 0; i < length; i++) {
870+
if (!toDelete[self[i] & 0xFF]) {
871+
result[resultLen] = self[i];
872+
resultLen++;
873+
}
874+
}
875+
if (resultLen == length) {
876+
return new Result(result, false);
877+
}
878+
return new Result(Arrays.copyOf(result, resultLen), true);
879+
}
880+
881+
private static Result translate(byte[] self, byte[] table) {
882+
final int length = self.length;
883+
byte[] result = new byte[length];
884+
boolean changed = false;
885+
for (int i = 0; i < length; i++) {
886+
byte b = table[self[i]];
887+
if (!changed && b != self[i]) {
888+
changed = true;
889+
}
890+
result[i] = b;
891+
}
892+
return new Result(result, changed);
893+
}
894+
895+
private static Result translateAndDelete(byte[] self, byte[] table, byte[] delete) {
896+
final int length = self.length;
897+
byte[] result = new byte[length];
898+
int resultLen = 0;
899+
boolean changed = false;
900+
boolean[] toDelete = createDeleteTable(delete);
901+
902+
for (int i = 0; i < length; i++) {
903+
if (!toDelete[self[i]]) {
904+
byte b = table[self[i]];
905+
if (!changed && b != self[i]) {
906+
changed = true;
907+
}
908+
result[resultLen] = b;
909+
resultLen++;
910+
}
911+
}
912+
if (resultLen == length) {
913+
return new Result(result, changed);
914+
}
915+
return new Result(Arrays.copyOf(result, resultLen), true);
916+
}
917+
918+
@Specialization(guards = "isNoValue(delete)")
919+
public PBytes translate(PBytes self, @SuppressWarnings("unused") PNone table, @SuppressWarnings("unused") PNone delete) {
920+
return self;
921+
}
922+
923+
@Specialization(guards = "isNoValue(delete)")
924+
public PByteArray translate(PByteArray self, @SuppressWarnings("unused") PNone table, @SuppressWarnings("unused") PNone delete) {
925+
return factory().createByteArray(self.getSequenceStorage().copy());
926+
}
927+
928+
@Specialization
929+
public PBytes translate(PBytes self, Object table, @SuppressWarnings("unused") PNone delete) {
930+
byte[] bTable = getToBytesNode().execute(table);
931+
checkLengthOfTable(bTable);
932+
byte[] bSelf = getToBytesNode().execute(self);
933+
934+
Result result = translate(bSelf, bTable);
935+
if (result.changed) {
936+
return factory().createBytes(result.array);
937+
}
938+
return self;
939+
}
940+
941+
@Specialization
942+
public PByteArray translate(PByteArray self, Object table, @SuppressWarnings("unused") PNone delete) {
943+
byte[] bTable = getToBytesNode().execute(table);
944+
checkLengthOfTable(bTable);
945+
byte[] bSelf = getToBytesNode().execute(self);
946+
947+
Result result = translate(bSelf, bTable);
948+
return factory().createByteArray(result.array);
949+
}
950+
951+
@Specialization(guards = "isNone(table)")
952+
public PBytes delete(PBytes self, @SuppressWarnings("unused") PNone table, Object delete) {
953+
byte[] bSelf = getToBytesNode().execute(self);
954+
byte[] bDelete = getToBytesNode().execute(delete);
955+
956+
Result result = delete(bSelf, bDelete);
957+
if (result.changed) {
958+
return factory().createBytes(result.array);
959+
}
960+
return self;
961+
}
962+
963+
@Specialization(guards = "isNone(table)")
964+
public PByteArray delete(PByteArray self, @SuppressWarnings("unused") PNone table, Object delete) {
965+
byte[] bSelf = getToBytesNode().execute(self);
966+
byte[] bDelete = getToBytesNode().execute(delete);
967+
968+
Result result = delete(bSelf, bDelete);
969+
return factory().createByteArray(result.array);
970+
}
971+
972+
@Specialization(guards = {"!isPNone(table)", "!isPNone(delete)"})
973+
public PBytes translateAndDelete(PBytes self, Object table, Object delete) {
974+
byte[] bTable = getToBytesNode().execute(table);
975+
checkLengthOfTable(bTable);
976+
byte[] bDelete = getToBytesNode().execute(delete);
977+
byte[] bSelf = getToBytesNode().execute(self);
978+
979+
Result result = translateAndDelete(bSelf, bTable, bDelete);
980+
if (result.changed) {
981+
return factory().createBytes(result.array);
982+
}
983+
return self;
984+
}
985+
986+
@Specialization(guards = {"!isPNone(table)", "!isPNone(delete)"})
987+
public PByteArray translateAndDelete(PByteArray self, Object table, Object delete) {
988+
byte[] bTable = getToBytesNode().execute(table);
989+
checkLengthOfTable(bTable);
990+
byte[] bDelete = getToBytesNode().execute(delete);
991+
byte[] bSelf = getToBytesNode().execute(self);
992+
993+
Result result = translateAndDelete(bSelf, bTable, bDelete);
994+
return factory().createByteArray(result.array);
995+
}
996+
}
809997
}

0 commit comments

Comments
 (0)