Skip to content

Commit 81b7c2a

Browse files
committed
Add Erlang/OTP 23 support for NEW_PID_EXT, NEW_PORT_EXT, NEWER_REFERENCE_EXT (erlang/otp@3c17104)
1 parent dce85e9 commit 81b7c2a

File tree

2 files changed

+138
-27
lines changed

2 files changed

+138
-27
lines changed

erlang.py

Lines changed: 79 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ def b_ord(character):
7979
_TAG_NEW_FLOAT_EXT = 70
8080
_TAG_BIT_BINARY_EXT = 77
8181
_TAG_ATOM_CACHE_REF = 78
82+
_TAG_NEW_PID_EXT = 88
83+
_TAG_NEW_PORT_EXT = 89
84+
_TAG_NEWER_REFERENCE_EXT = 90
8285
_TAG_SMALL_INTEGER_EXT = 97
8386
_TAG_INTEGER_EXT = 98
8487
_TAG_FLOAT_EXT = 99
@@ -274,10 +277,19 @@ def binary(self):
274277
"""
275278
return encoded representation
276279
"""
277-
return (
278-
b_chr(_TAG_PID_EXT) +
279-
self.node.binary() + self.id + self.serial + self.creation
280-
)
280+
creation_size = len(self.creation)
281+
if creation_size == 1:
282+
return (
283+
b_chr(_TAG_PID_EXT) +
284+
self.node.binary() + self.id + self.serial + self.creation
285+
)
286+
elif creation_size == 4:
287+
return (
288+
b_chr(_TAG_NEW_PID_EXT) +
289+
self.node.binary() + self.id + self.serial + self.creation
290+
)
291+
else:
292+
raise OutputException('unknown pid type')
281293
def __repr__(self):
282294
return '%s(%s,%s,%s,%s)' % (
283295
self.__class__.__name__,
@@ -303,10 +315,19 @@ def binary(self):
303315
"""
304316
return encoded representation
305317
"""
306-
return (
307-
b_chr(_TAG_PORT_EXT) +
308-
self.node.binary() + self.id + self.creation
309-
)
318+
creation_size = len(self.creation)
319+
if creation_size == 1:
320+
return (
321+
b_chr(_TAG_PORT_EXT) +
322+
self.node.binary() + self.id + self.creation
323+
)
324+
elif creation_size == 4:
325+
return (
326+
b_chr(_TAG_NEW_PORT_EXT) +
327+
self.node.binary() + self.id + self.creation
328+
)
329+
else:
330+
raise OutputException('unknown port type')
310331
def __repr__(self):
311332
return '%s(%s,%s,%s)' % (
312333
self.__class__.__name__,
@@ -338,11 +359,21 @@ def binary(self):
338359
self.node.binary() + self.id + self.creation
339360
)
340361
elif length <= 65535:
341-
return (
342-
b_chr(_TAG_NEW_REFERENCE_EXT) +
343-
struct.pack(b'>H', length) +
344-
self.node.binary() + self.creation + self.id
345-
)
362+
creation_size = len(self.creation)
363+
if creation_size == 1:
364+
return (
365+
b_chr(_TAG_NEW_REFERENCE_EXT) +
366+
struct.pack(b'>H', length) +
367+
self.node.binary() + self.creation + self.id
368+
)
369+
elif creation_size == 4:
370+
return (
371+
b_chr(_TAG_NEWER_REFERENCE_EXT) +
372+
struct.pack(b'>H', length) +
373+
self.node.binary() + self.creation + self.id
374+
)
375+
else:
376+
raise OutputException('unknown reference type')
346377
else:
347378
raise OutputException('uint16 overflow')
348379
def __repr__(self):
@@ -478,24 +509,33 @@ def _binary_to_term(i, data):
478509
j = struct.unpack(b'>H', data[i:i + 2])[0]
479510
i += 2
480511
return (i + j, OtpErlangAtom(data[i:i + j]))
481-
elif tag == _TAG_REFERENCE_EXT or tag == _TAG_PORT_EXT:
512+
elif (tag == _TAG_NEW_PORT_EXT or
513+
tag == _TAG_REFERENCE_EXT or tag == _TAG_PORT_EXT):
482514
i, node = _binary_to_atom(i, data)
483515
id_value = data[i:i + 4]
484516
i += 4
485-
creation = data[i:i + 1]
486-
i += 1
487-
if tag == _TAG_REFERENCE_EXT:
488-
return (i, OtpErlangReference(node, id_value, creation))
489-
# tag == _TAG_PORT_EXT
517+
if tag == _TAG_NEW_PORT_EXT:
518+
creation = data[i:i + 4]
519+
i += 4
520+
else:
521+
creation = data[i:i + 1]
522+
i += 1
523+
if tag == _TAG_REFERENCE_EXT:
524+
return (i, OtpErlangReference(node, id_value, creation))
525+
# tag == _TAG_NEW_PORT_EXT or tag == _TAG_PORT_EXT
490526
return (i, OtpErlangPort(node, id_value, creation))
491-
elif tag == _TAG_PID_EXT:
527+
elif tag == _TAG_NEW_PID_EXT or tag == _TAG_PID_EXT:
492528
i, node = _binary_to_atom(i, data)
493529
id_value = data[i:i + 4]
494530
i += 4
495531
serial = data[i:i + 4]
496532
i += 4
497-
creation = data[i:i + 1]
498-
i += 1
533+
if tag == _TAG_NEW_PID_EXT:
534+
creation = data[i:i + 4]
535+
i += 4
536+
elif tag == _TAG_PID_EXT:
537+
creation = data[i:i + 1]
538+
i += 1
499539
return (i, OtpErlangPid(node, id_value, serial, creation))
500540
elif tag == _TAG_SMALL_TUPLE_EXT or tag == _TAG_LARGE_TUPLE_EXT:
501541
if tag == _TAG_SMALL_TUPLE_EXT:
@@ -554,12 +594,16 @@ def _binary_to_term(i, data):
554594
_ = b_ord(data[i])
555595
i += 1
556596
return (i, OtpErlangFunction(tag, data[old_i:i]))
557-
elif tag == _TAG_NEW_REFERENCE_EXT:
597+
elif tag == _TAG_NEWER_REFERENCE_EXT or tag == _TAG_NEW_REFERENCE_EXT:
558598
j = struct.unpack(b'>H', data[i:i + 2])[0] * 4
559599
i += 2
560600
i, node = _binary_to_atom(i, data)
561-
creation = data[i:i + 1]
562-
i += 1
601+
if tag == _TAG_NEWER_REFERENCE_EXT:
602+
creation = data[i:i + 4]
603+
i += 4
604+
elif tag == _TAG_NEW_REFERENCE_EXT:
605+
creation = data[i:i + 1]
606+
i += 1
563607
return (i + j, OtpErlangReference(node, data[i: i + j], creation))
564608
elif tag == _TAG_SMALL_ATOM_EXT:
565609
j = b_ord(data[i])
@@ -648,7 +692,16 @@ def _binary_to_integer(i, data):
648692
def _binary_to_pid(i, data):
649693
tag = b_ord(data[i])
650694
i += 1
651-
if tag == _TAG_PID_EXT:
695+
if tag == _TAG_NEW_PID_EXT:
696+
i, node = _binary_to_atom(i, data)
697+
id_value = data[i:i + 4]
698+
i += 4
699+
serial = data[i:i + 4]
700+
i += 4
701+
creation = data[i:i + 4]
702+
i += 4
703+
return (i, OtpErlangPid(node, id_value, serial, creation))
704+
elif tag == _TAG_PID_EXT:
652705
i, node = _binary_to_atom(i, data)
653706
id_value = data[i:i + 4]
654707
i += 4

tests/erlang_tests.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#
55
# MIT License
66
#
7-
# Copyright (c) 2014-2018 Michael Truog <mjtruog at protonmail dot com>
7+
# Copyright (c) 2014-2019 Michael Truog <mjtruog at protonmail dot com>
88
# Copyright (c) 2009-2013 Dmitry Vasiliev <dima@hlabs.org>
99
#
1010
# Permission is hereby granted, free of charge, to any person obtaining a
@@ -270,6 +270,64 @@ def test_binary_to_term_big_integer(self):
270270
erlang.binary_to_term(b'\x83o\0\0\0\6\0\1\2\3\4\5\6'))
271271
self.assertEqual(-6618611909121,
272272
erlang.binary_to_term(b'\x83o\0\0\0\6\1\1\2\3\4\5\6'))
273+
def test_binary_to_term_pid(self):
274+
pid_old_binary = (
275+
b'\x83\x67\x64\x00\x0D\x6E\x6F\x6E\x6F\x64\x65\x40\x6E\x6F'
276+
b'\x68\x6F\x73\x74\x00\x00\x00\x4E\x00\x00\x00\x00\x00'
277+
)
278+
pid_old = erlang.binary_to_term(pid_old_binary)
279+
self.assertTrue(isinstance(pid_old, erlang.OtpErlangPid))
280+
self.assertEqual(erlang.term_to_binary(pid_old),
281+
b'\x83gs\rnonode@nohost\x00\x00\x00N'
282+
b'\x00\x00\x00\x00\x00')
283+
pid_new_binary = (
284+
b'\x83\x58\x64\x00\x0D\x6E\x6F\x6E\x6F\x64\x65\x40\x6E\x6F\x68'
285+
b'\x6F\x73\x74\x00\x00\x00\x4E\x00\x00\x00\x00\x00\x00\x00\x00'
286+
)
287+
pid_new = erlang.binary_to_term(pid_new_binary)
288+
self.assertTrue(isinstance(pid_new, erlang.OtpErlangPid))
289+
self.assertEqual(erlang.term_to_binary(pid_new),
290+
b'\x83Xs\rnonode@nohost\x00\x00\x00N'
291+
b'\x00\x00\x00\x00\x00\x00\x00\x00')
292+
def test_binary_to_term_port(self):
293+
port_old_binary = (
294+
b'\x83\x66\x64\x00\x0D\x6E\x6F\x6E\x6F\x64\x65\x40\x6E\x6F\x68'
295+
b'\x6F\x73\x74\x00\x00\x00\x06\x00'
296+
)
297+
port_old = erlang.binary_to_term(port_old_binary)
298+
self.assertTrue(isinstance(port_old, erlang.OtpErlangPort))
299+
self.assertEqual(erlang.term_to_binary(port_old),
300+
b'\x83fs\rnonode@nohost\x00\x00\x00\x06\x00')
301+
port_new_binary = (
302+
b'\x83\x59\x64\x00\x0D\x6E\x6F\x6E\x6F\x64\x65\x40\x6E\x6F\x68'
303+
b'\x6F\x73\x74\x00\x00\x00\x06\x00\x00\x00\x00'
304+
)
305+
port_new = erlang.binary_to_term(port_new_binary)
306+
self.assertTrue(isinstance(port_new, erlang.OtpErlangPort))
307+
self.assertEqual(erlang.term_to_binary(port_new),
308+
b'\x83Ys\rnonode@nohost\x00\x00\x00\x06'
309+
b'\x00\x00\x00\x00')
310+
def test_binary_to_term_ref(self):
311+
ref_new_binary = (
312+
b'\x83\x72\x00\x03\x64\x00\x0D\x6E\x6F\x6E\x6F\x64\x65\x40\x6E'
313+
b'\x6F\x68\x6F\x73\x74\x00\x00\x03\xE8\x4E\xE7\x68\x00\x02\xA4'
314+
b'\xC8\x53\x40'
315+
)
316+
ref_new = erlang.binary_to_term(ref_new_binary)
317+
self.assertTrue(isinstance(ref_new, erlang.OtpErlangReference))
318+
self.assertEqual(erlang.term_to_binary(ref_new),
319+
b'\x83r\x00\x03s\rnonode@nohost\x00\x00\x03\xe8'
320+
b'N\xe7h\x00\x02\xa4\xc8S@')
321+
ref_newer_binary = (
322+
b'\x83\x5A\x00\x03\x64\x00\x0D\x6E\x6F\x6E\x6F\x64\x65\x40\x6E'
323+
b'\x6F\x68\x6F\x73\x74\x00\x00\x00\x00\x00\x01\xAC\x03\xC7\x00'
324+
b'\x00\x04\xBB\xB2\xCA\xEE'
325+
)
326+
ref_newer = erlang.binary_to_term(ref_newer_binary)
327+
self.assertTrue(isinstance(ref_newer, erlang.OtpErlangReference))
328+
self.assertEqual(erlang.term_to_binary(ref_newer),
329+
b'\x83Z\x00\x03s\rnonode@nohost\x00\x00\x00\x00\x00'
330+
b'\x01\xac\x03\xc7\x00\x00\x04\xbb\xb2\xca\xee')
273331
def test_binary_to_term_compressed_term(self):
274332
self.assertRaises(erlang.ParseException,
275333
erlang.binary_to_term, b'\x83P')

0 commit comments

Comments
 (0)