summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRickard Green <rickard@erlang.org>2023-03-05 18:43:13 +0100
committerRickard Green <rickard@erlang.org>2023-03-15 13:05:24 +0100
commite6074fada29f7ec8a8ffd28e6a3aab75f74199f5 (patch)
treea882f19531a636db16ff1fec39fe3c99177bb0fe
parent2e9f3f57dc6b3c7e4ce48a9955abf16e3dc6c16d (diff)
downloaderlang-e6074fada29f7ec8a8ffd28e6a3aab75f74199f5.tar.gz
[erts] Option 'local' of term_to_binary/2 and term_to_iovec/2
Introduce a local external term format with an unspecified encoding. This external term format is used for supporting the 'local' option of term_to_binary/2 and term_to_iovec/2. Terms encoded on this format is only to be decoded by the same runtime system instance that encoded it.
-rw-r--r--erts/doc/src/erl_driver.xml6
-rw-r--r--erts/doc/src/erl_ext_dist.xml40
-rw-r--r--erts/doc/src/erlang.xml242
-rw-r--r--erts/emulator/beam/atom.c3
-rw-r--r--erts/emulator/beam/atom.names4
-rw-r--r--erts/emulator/beam/dist.h12
-rw-r--r--erts/emulator/beam/erl_init.c1
-rw-r--r--erts/emulator/beam/erl_term_hashing.c190
-rw-r--r--erts/emulator/beam/erl_term_hashing.h36
-rw-r--r--erts/emulator/beam/external.c795
-rw-r--r--erts/emulator/beam/external.h3
-rw-r--r--erts/emulator/beam/global.h1
-rw-r--r--erts/emulator/beam/sys.h1
-rw-r--r--erts/emulator/sys/unix/sys.c4
-rw-r--r--erts/emulator/sys/win32/sys.c9
-rw-r--r--erts/emulator/test/binary_SUITE.erl180
-rw-r--r--erts/emulator/test/binary_SUITE_data/Makefile.src33
-rw-r--r--erts/emulator/test/binary_SUITE_data/call_local_drv.c204
-rw-r--r--erts/emulator/test/binary_SUITE_data/send_term_local_drv.c96
-rw-r--r--erts/preloaded/ebin/erlang.beambin132784 -> 130784 bytes
-rw-r--r--erts/preloaded/src/erlang.erl12
-rw-r--r--lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c26
22 files changed, 1615 insertions, 283 deletions
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 2547b6e952..ef51504af4 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -2567,12 +2567,14 @@ ErlDrvTermData spec[] = {
ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
};
erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0])); ]]></code>
- <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
+ <p>
+ <marker id="ERL_DRV_EXT2TERM"/>
+ The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
term encoded with the
<seeguide marker="erl_ext_dist">external format</seeguide>,
that is, a term that has been encoded by
<seemfa marker="erlang#term_to_binary/2">
- <c>erlang:term_to_binary</c></seemfa>,
+ <c>erlang:term_to_binary()</c></seemfa>,
<seecref marker="erl_interface:ei"><c>erl_interface:ei(3)</c></seecref>,
and so on.
For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c>
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index 09c3cfc5c7..712c30afc0 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -1421,6 +1421,46 @@
</note>
</section>
+ <section>
+ <marker id="LOCAL_EXT"/>
+ <title>LOCAL_EXT</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">...</cell>
+ </row>
+ <row>
+ <cell align="center"><c>121</c></cell>
+ <cell align="center">...</cell>
+ </row>
+ <tcaption>LOCAL_EXT</tcaption></table>
+ <p>
+ Marks that this is encoded on an alternative local external term
+ format intended to only be decoded by a specific local decoder.
+ The bytes following from here on may contain any unspecified type
+ of encoding of terms. It is the responsibility of the user to only
+ attempt to decode terms on the local external term format which has
+ been produced by a matching encoder.
+ </p>
+ <p>
+ This tag is used by the Erlang runtime system upon encoding the local
+ external term format when the
+ <seeerl marker="erts:erlang#term_to_binary_local"><c>local</c></seeerl>
+ option is passed to
+ <seemfa marker="erts:erlang#term_to_binary/2"><c>term_to_binary/2</c></seemfa>,
+ but can be used by other encoders as well providing similar
+ functionality. The Erlang runtime system adds a hash immediately
+ following the <c>LOCAL_EXT</c> tag which is verified on decoding in
+ order to verify that encoder and decoder match which might be a good
+ practice. This will very likely catch mistakes made by users, but
+ is not guaranteed to, and is not intended to, prevent decoding of an
+ intentionally forged encoding on the local external term format.
+ </p>
+ <p>
+ <c>LOCAL_EXT</c> was introduced in OTP @OTP-18477@.
+ </p>
+ </section>
+
</chapter>
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index ff963047ca..d0bd54ea73 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -3324,7 +3324,7 @@ first</pre>
</list>
<p>A node
can also be alive if it has got a name from a call to <seemfa
- marker="kernel:net_kernel#start/1"><c>net_kernel:start/1</c></seemfa>
+ marker="kernel:net_kernel#start/2"><c>net_kernel:start/2</c></seemfa>
and has not been stopped by a call to <seemfa
marker="kernel:net_kernel#stop/0"><c>net_kernel:stop/0</c></seemfa>.</p>
</desc>
@@ -11846,60 +11846,192 @@ hello
<desc>
<p>Returns a binary data object that is the result of encoding
<c><anno>Term</anno></c> according to the Erlang external
- term format.</p>
- <p>If option <c>compressed</c> is provided, the external term
- format is compressed. The compressed format is automatically
- recognized by <c>binary_to_term/1</c> as from Erlang/OTP R7B.</p>
- <p>A compression level can be specified by giving option
- <c>{compressed, <anno>Level</anno>}</c>.
- <c><anno>Level</anno></c> is an integer
- with range 0..9, where:</p>
- <list type="bulleted">
- <item><p><c>0</c> - No compression is done (it is the same as
- giving no <c>compressed</c> option).</p></item>
- <item><p><c>1</c> - Takes least time but may not compress
- as well as the higher levels.</p></item>
- <item><p><c>6</c> - Default level when option <c>compressed</c>
- is provided.</p></item>
- <item><p><c>9</c> - Takes most time and tries to produce a smaller
- result. Notice "tries" in the preceding sentence; depending
- on the input term, level 9 compression either does or does
- not produce a smaller result than level 1 compression.</p></item>
- </list>
- <p>Option <c>{minor_version, <anno>Version</anno>}</c>
- can be used to control some
- encoding details. This option was introduced in Erlang/OTP R11B-4.
- The valid values for <c><anno>Version</anno></c> are:</p>
- <taglist>
- <tag><c>0</c></tag>
- <item>
- <p>Floats are encoded using a textual representation.</p>
- <p>Atoms that can be represented by a latin1 string are encoded
- using latin1 while only atoms that cannot be represented by latin1
- are encoded using utf8.</p>
- </item>
- <tag><c>1</c></tag>
- <item>
- <p>Floats are encoded in a more space-efficient and exact way
- (namely in the 64-bit IEEE format, rather than converted to a
- textual representation). As from Erlang/OTP R11B-4,
- <c>binary_to_term/1</c> can decode this representation.</p>
- <p>Atoms that can be represented by a latin1 string are encoded
- using latin1 while only atoms that cannot be represented by latin1
- are encoded using utf8.</p>
- </item>
- <tag><c>2</c></tag>
- <item>
- <p>This is as of Erlang/OTP 26.0 the <em>default</em>. Atoms are
- unconditionally encoded using utf8. Erlang/OTP systems as of R16B
- can decode this representation.</p>
- </item>
- </taglist>
- <p>Option <c>deterministic</c> (introduced in OTP 24.1) can
- be used to ensure that within the same major release of Erlang/OTP,
- the same encoded representation is returned for the same
- term. There is still no guarantee that the encoded representation
- remains the same between major releases of Erlang/OTP.</p>
+ term format.</p>
+ <p>Currently supported options:</p>
+ <taglist>
+ <tag><c>compressed</c></tag>
+ <item>
+ <p>
+ Compress the external term format. The compressed
+ format is automatically recognized by <c>binary_to_term/1</c>
+ as from Erlang/OTP R7B.
+ </p>
+ </item>
+ <tag><c>{compressed, <anno>Level</anno>}</c></tag>
+ <item>
+ <p>
+ Compress the external term format to a given level.
+ The compression level is specified by <c><anno>Level</anno></c>
+ which is an integer in the range 0..9, where:
+ </p>
+ <taglist>
+ <tag><c>0</c></tag>
+ <item><p>
+ No compression is done (it is the same as giving no
+ <c>compressed</c> option).
+ </p></item>
+ <tag><c>1</c></tag>
+ <item><p>
+ Takes least time but may not compress as well as the higher
+ levels.
+ </p></item>
+ <tag><c>6</c></tag>
+ <item><p>
+ Default level when option <c>compressed</c> is provided.
+ </p></item>
+ <tag><c>9</c></tag>
+ <item><p>
+ Takes most time and tries to produce a smaller result.
+ Notice "tries" in the preceding sentence; depending
+ on the input term, level 9 compression either does or does
+ not produce a smaller result than level 1 compression.
+ </p></item>
+ </taglist>
+ </item>
+ <tag since="R11B-4"><c>{minor_version, <anno>Version</anno>}</c></tag>
+ <item>
+ <p>
+ The option can be used to control some encoding details. Valid
+ values for <c><anno>Version</anno></c> are:
+ </p>
+ <taglist>
+ <tag><c>0</c></tag>
+ <item>
+ <p>Floats are encoded using a textual representation.</p>
+ <p>Atoms that can be represented by a latin1 string are encoded
+ using latin1 while only atoms that cannot be represented by latin1
+ are encoded using utf8.</p>
+ </item>
+ <tag><c>1</c></tag>
+ <item>
+ <p>Floats are encoded in a more space-efficient and exact way
+ (namely in the 64-bit IEEE format, rather than converted to a
+ textual representation). As from Erlang/OTP R11B-4,
+ <c>binary_to_term/1</c> can decode this representation.</p>
+ <p>Atoms that can be represented by a latin1 string are encoded
+ using latin1 while only atoms that cannot be represented by latin1
+ are encoded using utf8.</p>
+ </item>
+ <tag><c>2</c></tag>
+ <item>
+ <p>This is as of Erlang/OTP 26.0 the <em>default</em>. Atoms are
+ unconditionally encoded using utf8. Erlang/OTP systems as of R16B
+ can decode this representation.</p>
+ </item>
+ </taglist>
+ </item>
+ <tag since="OTP 24.1"><c>deterministic</c></tag>
+ <item>
+ <p>
+ This option can be used to ensure that, within the same major
+ release of Erlang/OTP, the same encoded representation is returned
+ for the same term. There is still no guarantee that the encoded
+ representation remains the same between major releases of
+ Erlang/OTP.
+ </p>
+ <p>
+ This option cannot be combined with the <c>local</c> option.
+ </p>
+ </item>
+ <tag since="OTP @OTP-18477@"><c>local</c>
+ <marker id="term_to_binary_local"/></tag>
+ <item>
+ <p>
+ This option will cause encoding of <c><anno>Term</anno></c>
+ to an alternative local version of the external term format which
+ when decoded by the same runtime system instance will produce a
+ term identical to the encoded term even when the node name and/or
+ <seeerl marker="#system_info_creation">creation</seeerl> of the
+ current runtime system instance have changed between encoding
+ and decoding. When encoding without the <c>local</c> option,
+ local identifiers such as <seetype marker="#pid">pids</seetype>,
+ <seetype marker="#port">ports</seetype> and
+ <seetype marker="#reference">references</seetype> will not
+ be the same if node name and/or creation of the current runtime
+ system instance changed between encoding and decoding. This since
+ such identifiers refer to a specific node by node name and
+ creation.
+ </p>
+ <p>
+ Node name and creation of a runtime system instance change
+ when the distribution is started or stopped. The distribution is
+ started when the runtime system is started using the
+ <seecom marker="erl#name"><c>-name</c></seecom> or
+ <seecom marker="erl#sname"><c>-sname</c></seecom> command line
+ arguments. Note that the actual start of the distribution happens
+ after other code in the startup phase has begun executing. The
+ distribution can also be started by calling
+ <seemfa marker="kernel:net_kernel#start/2"><c>net_kernel:start/2</c></seemfa>
+ and stopped by calling
+ <seemfa marker="kernel:net_kernel#stop/0"><c>net_kernel:stop/1</c></seemfa>
+ if it has not been started via the command line.
+ </p>
+ <p>
+ The decoding of a term encoded with the <c>local</c> option,
+ using for example
+ <seemfa marker="#term_to_binary/1"><c>binary_to_term()</c></seemfa>,
+ will try to verify that the term actually was encoded by the same
+ runtime system instance, and will in the vast majority of cases
+ fail if the encoding was performed by another runtime system
+ instance. You should however <em>not</em> trust that this
+ verification will work in all cases. You <em>should</em> make
+ sure to <em>only</em> decode terms encoded with the <c>local</c>
+ option on the same Erlang runtime system instance as the one that
+ encoded the terms.
+ </p>
+ <p>
+ Since it is only the runtime system that encoded a term using
+ the <c>local</c> option that can decode it, the local encoding
+ is typically pieced together with something else to produce a
+ reply to where the <c>local</c> encoding originates from.
+ If a term encoded using the <c>local</c> option is stripped of
+ its leading version number, it can be added as part
+ of a larger term (for example as an element in a tuple) when
+ encoding on the external term format using, for example,
+ <seecref marker="erl_interface:ei">ei</seecref>. In the <c>ei</c>
+ case, you would strip it of the version number using
+ <seecref marker="erl_interface:ei#ei_decode_version"><c>ei_decode_version()</c></seecref>
+ and then add the remaining local encoding to what you are encoding
+ using for example
+ <seecref marker="erl_interface:ei#ei_x_append_buf"><c>ei_x_append_buf()</c></seecref>.
+ </p>
+ <p>
+ A good example of when you want to use the <c>local</c> option, is
+ when you want to make a request from a process to a port
+ <seecref marker="erl_driver">driver</seecref> and utilize the
+ <seeguide marker="system/efficiency_guide:processes#receiving-messages">selective
+ receive optimization</seeguide> when receiving the reply. In
+ this scenario you want to create a reference, serialize the
+ reference on the external term format using the <c>local</c>
+ option, pass this to the driver in the request, and then wait
+ for the reply message in a selective receive matching on the
+ reference. The driver should send the reply using either
+ <seecref marker="erl_driver#erl_drv_output_term"><c>erl_drv_output_term()</c></seecref>
+ or
+ <seecref marker="erl_driver#erl_drv_send_term"><c>erl_drv_send_term()</c></seecref>
+ using the term type
+ <seecref marker="erl_driver#ERL_DRV_EXT2TERM"><c>ERL_DRV_EXT2TERM</c></seecref>
+ for the, in the request, previously received reference on the
+ external term format. Note that you should not strip the
+ leading version number from the local encoding when using the
+ term type <c>ERL_DRV_EXT2TERM</c> of this functionality. If you
+ in this example do not encode the reference using the <c>local</c>
+ option, and the distribution is started or stopped while the
+ request is ongoing, the process that made the request will hang
+ indefinitely since the reference in the reply message will never
+ match.
+ </p>
+ <p>
+ This option cannot be combined with the <c>deterministic</c>
+ option.
+ </p>
+ <p>
+ For more information see the
+ <seeguide marker="erl_ext_dist#LOCAL_EXT"><c>LOCAL_EXT</c></seeguide>
+ tag in the documentation of the external term format.
+ </p>
+ </item>
+ </taglist>
<p>See also <seemfa marker="#binary_to_term/1">
<c>binary_to_term/1</c></seemfa>.</p>
</desc>
diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c
index ffeeb664ad..a0e5c488b1 100644
--- a/erts/emulator/beam/atom.c
+++ b/erts/emulator/beam/atom.c
@@ -197,7 +197,6 @@ atom_alloc(Atom* tmpl)
static void
atom_free(Atom* obj)
{
- ASSERT(obj->slot.index == atom_val(am_ErtsSecretAtom));
}
static void latin1_to_utf8(byte* conv_buf, Uint buf_sz,
@@ -509,8 +508,6 @@ init_atom_table(void)
atom_tab(ix)->name = (byte*)erl_atom_names[i];
}
- /* Hide am_ErtsSecretAtom */
- hash_erase(&erts_atom_table.htable, atom_tab(atom_val(am_ErtsSecretAtom)));
}
void
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 6818a372f8..f0fa834d49 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -59,10 +59,6 @@ atom nocatch
atom undefined_function
atom undefined_lambda
-# Secret internal atom that can never be found by string lookup
-# and should never leak out to be seen by the user.
-atom ErtsSecretAtom='3RT$'
-
#
# Other commonly used atoms.
#
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index 29982ed2a5..475d87a09f 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -63,6 +63,7 @@
#define DFLAG_NAME_ME (((Uint64)0x2) << 32)
#define DFLAG_V4_NC (((Uint64)0x4) << 32)
#define DFLAG_ALIAS (((Uint64)0x8) << 32)
+#define DFLAG_LOCAL_EXT (((Uint64)0x10) << 32) /* internal */
/*
* In term_to_binary/2, we will use DFLAG_ATOM_CACHE to mean
@@ -282,10 +283,17 @@ typedef struct TTBEncodeContext_ {
SysIOVec* iov;
ErlDrvBinary** binv;
Eterm *termv;
- int iovec;
Uint fragment_size;
Sint frag_ix;
ErlIOVec *fragment_eiovs;
+ int iovec;
+ int continue_make_lext_hash;
+ int lext_vlen;
+ byte *lext_hash;
+ union {
+ ErtsBlockHashState block;
+ ErtsIovBlockHashState iov_block;
+ } lext_state;
#ifdef DEBUG
int debug_fragments;
int debug_vlen;
@@ -305,6 +313,8 @@ typedef struct TTBEncodeContext_ {
(Ctx)->iov = NULL; \
(Ctx)->binv = NULL; \
(Ctx)->fragment_size = ~((Uint) 0); \
+ (Ctx)->continue_make_lext_hash = 0; \
+ (Ctx)->lext_vlen = -1; \
if ((Flags) & DFLAG_PENDING_CONNECT) { \
(Ctx)->hopefull_flags = 0; \
(Ctx)->hopefull_flagsp = NULL; \
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 7ec1e3919c..7df1e6a841 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -388,6 +388,7 @@ erl_init(int ncpu,
erl_nif_init();
erts_msacc_init();
beamfile_init();
+ erts_late_init_external();
}
static Eterm
diff --git a/erts/emulator/beam/erl_term_hashing.c b/erts/emulator/beam/erl_term_hashing.c
index 0a894d4d2b..b31abceaf9 100644
--- a/erts/emulator/beam/erl_term_hashing.c
+++ b/erts/emulator/beam/erl_term_hashing.c
@@ -424,10 +424,6 @@ tail_recur:
#define HCONST 0x9e3779b9UL /* the golden ratio; an arbitrary value */
-typedef struct {
- Uint32 a,b,c;
-} ErtsBlockHashHelperCtx;
-
#define BLOCK_HASH_BYTES_PER_ITER 12
/* The three functions below are separated into different functions even
@@ -508,6 +504,192 @@ block_hash(byte *block, Uint block_length, Uint32 initval)
&ctx);
}
+/*
+ * Note! erts_block_hash() and erts_iov_block_hash() *must* produce
+ * the same result if the I/O vector is flattened and contain the
+ * same bytes as the array.
+ */
+
+void
+erts_block_hash_init(ErtsBlockHashState *state, const byte *ptr,
+ Uint len, Uint32 initval)
+{
+ block_hash_setup(initval, &state->hctx);
+ state->ptr = ptr;
+ state->len = len;
+ state->tot_len = len;
+}
+
+int
+erts_block_hash(Uint32 *hashp, Uint *sizep, ErtsBlockHashState *state)
+{
+ byte *ptr = (byte *) state->ptr;
+ Uint len = state->len;
+ Sint flen;
+ Uint llen;
+
+ do {
+
+ if (*sizep < len) {
+ llen = *sizep;
+ llen -= llen % BLOCK_HASH_BYTES_PER_ITER;
+ if (len > llen + BLOCK_HASH_BYTES_PER_ITER) {
+ llen += BLOCK_HASH_BYTES_PER_ITER;
+ flen = -1;
+ break;
+ }
+ }
+
+ /* do it all... */
+ flen = len % BLOCK_HASH_BYTES_PER_ITER;
+ llen = len - flen;
+
+ } while (0);
+
+ block_hash_buffer(ptr, llen, &state->hctx);
+
+ ptr += llen;
+
+ if (flen < 0) {
+ state->ptr = ptr;
+ state->len -= llen;
+ *sizep = llen;
+ return 0; /* yield */
+ }
+
+ *hashp = block_hash_final_bytes(ptr, (Uint) flen,
+ state->tot_len, &state->hctx);
+
+ *sizep = llen + flen;
+
+ state->ptr = ptr + flen;
+ state->len = 0;
+
+ return !0; /* done */
+}
+
+/*
+ * Note! erts_block_hash() and erts_iov_block_hash() *must* produce
+ * the same result if the I/O vector is flattened and contain the
+ * same bytes as the array.
+ */
+
+void
+erts_iov_block_hash_init(ErtsIovBlockHashState *state, SysIOVec *iov,
+ Uint vlen, Uint32 initval)
+{
+ block_hash_setup(initval, &state->hctx);
+ state->iov = iov;
+ state->vlen = vlen;
+ state->tot_len = 0;
+ state->vix = 0;
+ state->ix = 0;
+}
+
+int
+erts_iov_block_hash(Uint32 *hashp, Uint *sizep, ErtsIovBlockHashState *state)
+{
+ byte buf[BLOCK_HASH_BYTES_PER_ITER];
+ ErtsBlockHashHelperCtx *hctx = &state->hctx;
+ SysIOVec *iov = state->iov;
+ Uint vlen = state->vlen;
+ int vix = state->vix;
+ int ix = state->ix;
+ Uint cix = 0;
+ byte *final_bytes;
+ Uint no_final_bytes;
+ Uint chunk_sz = (*sizep
+ - *sizep % BLOCK_HASH_BYTES_PER_ITER
+ + BLOCK_HASH_BYTES_PER_ITER);
+
+ do {
+ Uint bsz, csz;
+ int left;
+ byte *ptr;
+
+ ASSERT((cix % BLOCK_HASH_BYTES_PER_ITER) == 0);
+
+ /*
+ * We may have empty vectors...
+ */
+ while (ix == iov[vix].iov_len) {
+ vix++;
+ if (vix == vlen) {
+ final_bytes = NULL;
+ no_final_bytes = 0;
+ goto finalize;
+ }
+ ix = 0;
+ }
+
+ csz = chunk_sz - cix;
+ left = iov[vix].iov_len - ix;
+ ptr = iov[vix].iov_base;
+
+ if (left >= BLOCK_HASH_BYTES_PER_ITER) {
+ if (csz <= left)
+ bsz = csz;
+ else
+ bsz = left - (left % BLOCK_HASH_BYTES_PER_ITER);
+ block_hash_buffer(ptr + ix, bsz, hctx);
+ cix += bsz;
+ ix += bsz;
+ }
+ else {
+ int bix = 0;
+ bsz = left;
+ while (!0) {
+ sys_memcpy(&buf[bix], ptr + ix, bsz);
+ bix += bsz;
+ cix += bsz;
+ ix += bsz;
+ if (bix == BLOCK_HASH_BYTES_PER_ITER) {
+ block_hash_buffer(&buf[0],
+ (Uint) BLOCK_HASH_BYTES_PER_ITER,
+ hctx);
+ break;
+ }
+ ASSERT(ix == iov[vix].iov_len);
+ vix++;
+ if (vix == vlen) {
+ final_bytes = &buf[0];
+ no_final_bytes = (Uint) bsz;
+ goto finalize;
+ }
+ ix = 0;
+ ptr = iov[vix].iov_base;
+ bsz = iov[vix].iov_len;
+ if (bsz > BLOCK_HASH_BYTES_PER_ITER - bix)
+ bsz = BLOCK_HASH_BYTES_PER_ITER - bix;
+ }
+ }
+
+ } while (cix < chunk_sz);
+
+ ASSERT((cix % BLOCK_HASH_BYTES_PER_ITER) == 0);
+
+ /* yield */
+
+ *sizep = cix;
+
+ state->tot_len += cix;
+ state->vix = vix;
+ state->ix = ix;
+
+ return 0;
+
+finalize:
+
+ state->tot_len += cix;
+ *sizep = cix;
+
+ *hashp = block_hash_final_bytes(final_bytes, no_final_bytes,
+ state->tot_len, hctx);
+ return !0; /* done */
+}
+
+
+
typedef enum {
tag_primary_list,
arityval_subtag,
diff --git a/erts/emulator/beam/erl_term_hashing.h b/erts/emulator/beam/erl_term_hashing.h
index 79c28d52bd..db5c240a0a 100644
--- a/erts/emulator/beam/erl_term_hashing.h
+++ b/erts/emulator/beam/erl_term_hashing.h
@@ -22,16 +22,52 @@
#define ERL_TERM_HASHING_H__
#include "sys.h"
+#include "erl_drv_nif.h"
#if (defined(__aarch64__) && defined(__ARM_FEATURE_CRC32)) || \
(defined(__x86_64__) && defined(__SSE4_2__))
# define ERL_INTERNAL_HASH_CRC32C
#endif
+typedef struct {
+ Uint32 a,b,c;
+} ErtsBlockHashHelperCtx;
+
+typedef struct {
+ ErtsBlockHashHelperCtx hctx;
+ const byte *ptr;
+ Uint len;
+ Uint tot_len;
+} ErtsBlockHashState;
+
+typedef struct {
+ ErtsBlockHashHelperCtx hctx;
+ SysIOVec* iov;
+ Uint vlen;
+ Uint tot_len;
+ int vix;
+ int ix;
+} ErtsIovBlockHashState;
+
Uint32 make_hash2(Eterm);
Uint32 trapping_make_hash2(Eterm, Eterm*, struct process*);
Uint32 make_hash(Eterm);
Uint32 make_internal_hash(Eterm, Uint32 salt);
Uint32 make_map_hash(Eterm key, int level);
+void erts_block_hash_init(ErtsBlockHashState *state,
+ const byte *ptr,
+ Uint len,
+ Uint32 initval);
+int erts_block_hash(Uint32 *hashp,
+ Uint *sizep,
+ ErtsBlockHashState *state);
+void erts_iov_block_hash_init(ErtsIovBlockHashState *state,
+ SysIOVec *iov,
+ Uint vlen,
+ Uint32 initval);
+int erts_iov_block_hash(Uint32 *hashp,
+ Uint *sizep,
+ ErtsIovBlockHashState *state);
+
#endif
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index bfa08e07bc..a380454699 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -47,6 +47,8 @@
#include "erl_proc_sig_queue.h"
#include "erl_trace.h"
#include "erl_global_literals.h"
+#include "erl_term_hashing.h"
+
#define PASS_THROUGH 'p'
@@ -95,8 +97,8 @@ static byte* enc_atom(ErtsAtomCacheMap *, Eterm, byte*, Uint64);
static byte* enc_pid(ErtsAtomCacheMap *, Eterm, byte*, Uint64);
struct B2TContext_t;
static const byte* dec_term(ErtsDistExternal*, ErtsHeapFactory*, const byte*, Eterm*, struct B2TContext_t*, int);
-static const byte* dec_atom(ErtsDistExternal *, const byte*, Eterm*);
-static const byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, const byte*, Eterm*, byte tag);
+static const byte* dec_atom(ErtsDistExternal *, const byte*, Eterm*, int);
+static const byte* dec_pid(ErtsDistExternal *, ErtsHeapFactory*, const byte*, Eterm*, byte tag, int);
static Sint decoded_size(const byte *ep, const byte* endp, int internal_tags, struct B2TContext_t*);
static BIF_RETTYPE term_to_binary_trap_1(BIF_ALIST_1);
@@ -104,7 +106,6 @@ static Eterm erts_term_to_binary_int(Process* p, Sint bif_ix, Eterm Term, Eterm
Uint64 dflags, Binary *context_b, int iovec,
Uint fragment_size);
-static Uint encode_size_struct2(ErtsAtomCacheMap *, Eterm, Uint64);
static ErtsExtSzRes encode_size_struct_int(TTBSizeContext*, ErtsAtomCacheMap *acmp,
Eterm obj, Uint64 dflags, Sint *reds, Uint *res);
@@ -126,6 +127,72 @@ void erts_init_external(void) {
return;
}
+static Uint32 local_node_hash;
+
+void erts_late_init_external(void)
+{
+ char hname[256], pidstr[21];
+ size_t hname_len, pidstr_len;
+ ErtsMonotonicTime mtime, toffs;
+ ErtsBlockHashState hstate;
+ byte *lnid;
+ Uint lnid_ix, chunk_size;
+ int res;
+
+ res = sys_get_hostname(&hname[0], sizeof(hname));
+ if (res == 0) {
+ hname_len = strlen(hname);
+ }
+ else {
+ hname[0] = '\0';
+ hname_len = 0;
+ }
+
+ sys_get_pid(&pidstr[0], sizeof(pidstr));
+ pidstr[20] = '\0';
+
+ pidstr_len = strlen(pidstr);
+
+ toffs = erts_get_time_offset();
+ mtime = erts_get_monotonic_time(NULL);
+
+ lnid = (byte *) erts_alloc(ERTS_ALC_T_TMP, 8 + hname_len + pidstr_len);
+
+ lnid_ix = 0;
+
+ /* time offset... */
+ lnid[lnid_ix++] = (byte) toffs & 0xff;
+ lnid[lnid_ix++] = (byte) (toffs >> 8) & 0xff;
+ lnid[lnid_ix++] = (byte) (toffs >> 16) & 0xff;
+ lnid[lnid_ix++] = (byte) (toffs >> 24) & 0xff;
+ lnid[lnid_ix++] = (byte) (toffs >> 32) & 0xff;
+ lnid[lnid_ix++] = (byte) (toffs >> 40) & 0xff;
+ lnid[lnid_ix++] = (byte) (toffs >> 48) & 0xff;
+ lnid[lnid_ix++] = (byte) (toffs >> 56) & 0xff;
+
+ /* hostname... */
+
+ sys_memcpy(&lnid[lnid_ix], &hname[0], hname_len);
+
+ lnid_ix += hname_len;
+
+ /* pid... */
+ memcpy(&lnid[lnid_ix], &pidstr[0], pidstr_len);
+
+ /*
+ * Use least significant 32 bits of monotonic time as initial
+ * value to hash...
+ */
+ erts_block_hash_init(&hstate, &lnid[0], lnid_ix,
+ (Uint32) (mtime & 0xffffffff));
+ chunk_size = ERTS_UINT_MAX;
+ res = erts_block_hash(&local_node_hash, &chunk_size, &hstate);
+ ASSERT(res); (void) res;
+ ASSERT(chunk_size == lnid_ix);
+
+ erts_free(ERTS_ALC_T_TMP, lnid);
+}
+
#define ERTS_MAX_INTERNAL_ATOM_CACHE_ENTRIES 255
#define ERTS_DIST_HDR_ATOM_CACHE_FLAG_BYTE_IX(IIX) \
@@ -659,7 +726,7 @@ erts_encode_dist_ext_size(Eterm term,
return res;
}
-ErtsExtSzRes erts_encode_ext_size_2(Eterm term, unsigned dflags, Uint *szp)
+ErtsExtSzRes erts_encode_ext_size_2(Eterm term, Uint64 dflags, Uint *szp)
{
ErtsExtSzRes res;
*szp = 0;
@@ -675,8 +742,14 @@ ErtsExtSzRes erts_encode_ext_size(Eterm term, Uint *szp)
Uint erts_encode_ext_size_ets(Eterm term)
{
- return encode_size_struct2(NULL, term,
- TERM_TO_BINARY_DFLAGS|DFLAG_ETS_COMPRESSED);
+ ErtsExtSzRes res;
+ Uint sz = 0;
+
+ res = encode_size_struct_int(NULL, NULL, term,
+ TERM_TO_BINARY_DFLAGS|DFLAG_ETS_COMPRESSED,
+ NULL, &sz);
+ ASSERT(res == ERTS_EXT_SZ_OK); (void) res;
+ return sz;
}
@@ -1400,12 +1473,12 @@ BIF_RETTYPE term_to_iovec_1(BIF_ALIST_1)
}
static ERTS_INLINE int
-parse_t2b_opts(Eterm opts, Uint *flagsp, int *levelp, int *iovecp, Uint *fsizep)
+parse_t2b_opts(Eterm opts, Uint64 *flagsp, int *levelp, int *iovecp, Uint *fsizep)
{
int level = 0;
int iovec = 0;
- Uint flags = TERM_TO_BINARY_DFLAGS;
- int deterministic = 0;
+ Uint64 flags = TERM_TO_BINARY_DFLAGS;
+ int deterministic = 0, local = 0;
Uint fsize = ~((Uint) 0); /* one fragment */
while (is_list(opts)) {
@@ -1418,6 +1491,8 @@ parse_t2b_opts(Eterm opts, Uint *flagsp, int *levelp, int *iovecp, Uint *fsizep)
iovec = !0;
} else if (arg == am_deterministic) {
deterministic = 1;
+ } else if (arg == am_local) {
+ local = !0;
} else if (is_tuple(arg) && *(tp = tuple_val(arg)) == make_arityval(2)) {
if (tp[1] == am_minor_version && is_small(tp[2])) {
switch (signed_val(tp[2])) {
@@ -1460,6 +1535,14 @@ parse_t2b_opts(Eterm opts, Uint *flagsp, int *levelp, int *iovecp, Uint *fsizep)
return 0; /* badarg */
}
+ if (deterministic && local) {
+ return 0; /* badarg */
+ }
+
+ if (local) {
+ flags |= DFLAG_LOCAL_EXT;
+ }
+
if (deterministic) {
flags |= DFLAG_DETERMINISTIC;
}
@@ -1477,7 +1560,7 @@ parse_t2b_opts(Eterm opts, Uint *flagsp, int *levelp, int *iovecp, Uint *fsizep)
BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
{
int level;
- Uint flags;
+ Uint64 flags;
Eterm res;
if (!parse_t2b_opts(BIF_ARG_2, &flags, &level, NULL, NULL)) {
@@ -1504,7 +1587,7 @@ BIF_RETTYPE term_to_binary_2(BIF_ALIST_2)
BIF_RETTYPE term_to_iovec_2(BIF_ALIST_2)
{
int level;
- Uint flags;
+ Uint64 flags;
Eterm res;
if (!parse_t2b_opts(BIF_ARG_2, &flags, &level, NULL, NULL)) {
@@ -1533,7 +1616,7 @@ erts_debug_term_to_binary(Process *p, Eterm term, Eterm opts)
{
Eterm ret;
int level, iovec;
- Uint flags;
+ Uint64 flags;
Uint fsize;
if (!parse_t2b_opts(opts, &flags, &level, &iovec, &fsize)) {
@@ -1580,9 +1663,12 @@ enum B2TState { /* order is somewhat significant */
typedef struct {
Sint heap_size;
- int terms;
- const byte* ep;
+ Uint32 terms;
int atom_extra_skip;
+ const byte* ep;
+ ErtsBlockHashState lext_state;
+ const byte *lext_hash;
+ Uint32 lext_term_end;
} B2TSizeContext;
typedef struct {
@@ -1591,6 +1677,7 @@ typedef struct {
Eterm* next;
ErtsHeapFactory factory;
int remaining_n;
+ int internal_nc;
char* remaining_bytes;
ErtsPStack map_array;
} B2TDecodeContext;
@@ -1820,7 +1907,6 @@ static BIF_RETTYPE binary_to_term_trap_1(BIF_ALIST_1)
return binary_to_term_int(BIF_P, THE_NON_VALUE, ERTS_MAGIC_BIN_DATA(context_bin));
}
-
#define B2T_BYTES_PER_REDUCTION 128
#define B2T_MEMCPY_FACTOR 8
@@ -1947,6 +2033,7 @@ static BIF_RETTYPE binary_to_term_int(Process* p, Eterm bin, B2TContext *ctx)
ctx->u.dc.ep = ctx->b2ts.extp;
ctx->u.dc.res = (Eterm) (UWord) NULL;
ctx->u.dc.next = &ctx->u.dc.res;
+ ctx->u.dc.internal_nc = 0;
erts_factory_proc_prealloc_init(&ctx->u.dc.factory, p, ctx->heap_size);
ctx->u.dc.map_array.pstart = NULL;
ctx->state = B2TDecode;
@@ -2118,34 +2205,11 @@ Eterm
external_size_2(BIF_ALIST_2)
{
Uint size = 0;
- Uint flags = TERM_TO_BINARY_DFLAGS;
-
- while (is_list(BIF_ARG_2)) {
- Eterm arg = CAR(list_val(BIF_ARG_2));
- Eterm* tp;
+ int level;
+ Uint64 flags;
- if (is_tuple(arg) && *(tp = tuple_val(arg)) == make_arityval(2)) {
- if (tp[1] == am_minor_version && is_small(tp[2])) {
- switch (signed_val(tp[2])) {
- case 0:
- flags &= ~DFLAG_NEW_FLOATS;
- break;
- case 1:
- break;
- default:
- goto error;
- }
- } else {
- goto error;
- }
- } else {
- error:
- BIF_ERROR(BIF_P, BADARG);
- }
- BIF_ARG_2 = CDR(list_val(BIF_ARG_2));
- }
- if (is_not_nil(BIF_ARG_2)) {
- goto error;
+ if (!parse_t2b_opts(BIF_ARG_2, &flags, &level, NULL, NULL)) {
+ BIF_ERROR(BIF_P, BADARG);
}
switch (erts_encode_ext_size_2(BIF_ARG_1, flags, &size)) {
@@ -2817,45 +2881,80 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint64 dflags)
}
/*
- * We use this atom as sysname in local pid/port/refs
- * for the ETS compressed format
+ * We use INTERNAL_LOCAL_SYSNAME to mark internal node for pid/port/refs
+ * in the ETS compressed format and local format.
*
*/
-#define INTERNAL_LOCAL_SYSNAME am_ErtsSecretAtom
+#define INTERNAL_LOCAL_SYSNAME NIL
-static byte*
-enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint64 dflags)
+static ERTS_INLINE byte *
+enc_internal_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint64 dflags)
{
- Uint on, os;
- Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_ETS_COMPRESSED))
- ? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid));
- Uint32 creation = pid_creation(pid);
+ Uint32 number, serial, creation;
- *ep++ = NEW_PID_EXT;
+ ASSERT(is_internal_pid(pid));
- ep = enc_atom(acmp, sysname, ep, dflags);
+ *ep++ = NEW_PID_EXT;
- if (is_internal_pid(pid)) {
- on = internal_pid_number(pid);
- os = internal_pid_serial(pid);
+ number = internal_pid_number(pid);
+ serial = internal_pid_serial(pid);
+ if (dflags & (DFLAG_ETS_COMPRESSED|DFLAG_LOCAL_EXT)) {
+ *ep++ = NIL_EXT; /* INTERNAL_LOCAL_NODE */
+ creation = 0;
}
else {
- on = external_pid_number(pid);
- os = external_pid_serial(pid);
+ Eterm sysname = internal_pid_node_name(pid);
+ creation = internal_pid_creation(pid);
+ ep = enc_atom(acmp, sysname, ep, dflags);
}
- put_int32(on, ep);
+ put_int32(number, ep);
+ ep += 4;
+ put_int32(serial, ep);
+ ep += 4;
+ put_int32(creation, ep);
+ ep += 4;
+ return ep;
+
+}
+
+static ERTS_INLINE byte *
+enc_external_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint64 dflags)
+{
+ Uint32 number, serial, creation;
+ Eterm sysname;
+
+ ASSERT(is_external_pid(pid));
+
+ *ep++ = NEW_PID_EXT;
+
+ ASSERT(is_external_pid(pid));
+ number = external_pid_number(pid);
+ serial = external_pid_serial(pid);
+ sysname = external_pid_node_name(pid);
+ creation = external_pid_creation(pid);
+ ep = enc_atom(acmp, sysname, ep, dflags);
+
+ put_int32(number, ep);
ep += 4;
- put_int32(os, ep);
+ put_int32(serial, ep);
ep += 4;
put_int32(creation, ep);
ep += 4;
return ep;
}
+static byte*
+enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint64 dflags)
+{
+ if (is_internal_pid(pid))
+ return enc_internal_pid(acmp, pid, ep, dflags);
+ return enc_external_pid(acmp, pid, ep, dflags);
+}
+
/* Expect an atom in plain text or cached */
static const byte*
-dec_atom(ErtsDistExternal *edep, const byte* ep, Eterm* objp)
+dec_atom(ErtsDistExternal *edep, const byte* ep, Eterm* objp, int internal_nc)
{
Uint len;
int n;
@@ -2920,7 +3019,12 @@ dec_atom(ErtsDistExternal *edep, const byte* ep, Eterm* objp)
}
*objp = make_atom(n);
break;
-
+ case NIL_EXT:
+ if (internal_nc) {
+ *objp = INTERNAL_LOCAL_SYSNAME;
+ break;
+ }
+ /* else: fail... */
default:
error:
*objp = NIL; /* Don't leave a hole in the heap */
@@ -2948,7 +3052,7 @@ static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation, Eterm b
static const byte*
dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, const byte* ep,
- Eterm* objp, byte tag)
+ Eterm* objp, byte tag, int internal_nc)
{
Eterm sysname;
Uint data;
@@ -2959,7 +3063,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, const byte* ep,
*objp = NIL; /* In case we fail, don't leave a hole in the heap */
/* eat first atom */
- if ((ep = dec_atom(edep, ep, &sysname)) == NULL)
+ if ((ep = dec_atom(edep, ep, &sysname, internal_nc)) == NULL)
return NULL;
num = get_uint32(ep);
ep += 4;
@@ -3073,6 +3177,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
FloatDef f;
register Sint r = 0;
int use_iov = 0;
+ byte *lext_hash = NULL; /* initialize to avoid faulty warning... */
/* The following variables are only used during encoding of
* a map when the `deterministic` option is active. */
@@ -3090,10 +3195,39 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
obj = ctx->obj;
map_array = ctx->map_array;
next_map_element = ctx->next_map_element;
+ lext_hash = ctx->lext_hash;
if (is_non_value(obj)) {
goto outer_loop;
}
+ else {
+ goto L_jump_start;
+ }
}
+ if (ctx->continue_make_lext_hash) {
+ lext_hash = ctx->lext_hash;
+ ep = ctx->ep;
+ if (use_iov) {
+ goto continue_make_lext_hash_iov;
+ }
+ else {
+ goto continue_make_lext_hash_bin;
+ }
+ }
+ }
+
+ /* We only pass here once at the start of the encoding... */
+ if (dflags & DFLAG_LOCAL_EXT) {
+ *ep++ = LOCAL_EXT;
+ lext_hash = ep;
+ if (ctx)
+ ctx->lext_hash = ep;
+ ep += 4; /* 32-bit hash */
+ if (use_iov) {
+ ASSERT(ctx);
+ store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
+ /* current vlen is now where to start calculating the hash */
+ ctx->lext_vlen = ctx->vlen;
+ }
}
goto L_jump_start;
@@ -3362,54 +3496,89 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
}
break;
- case PID_DEF:
case EXTERNAL_PID_DEF:
- ep = enc_pid(acmp, obj, ep, dflags);
+ ep = enc_external_pid(acmp, obj, ep, dflags);
+ break;
+
+ case PID_DEF:
+ ep = enc_internal_pid(acmp, obj, ep, dflags);
break;
- case REF_DEF:
case EXTERNAL_REF_DEF: {
+ Eterm sysname;
Uint32 *ref_num;
- Eterm sysname = (((dflags & DFLAG_ETS_COMPRESSED) && is_internal_ref(obj))
- ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj));
- Uint32 creation = ref_creation(obj);
+ Uint32 creation;
+
+ *ep++ = NEWER_REFERENCE_EXT;
+
+ ref_num = external_ref_numbers(obj);
+ i = external_ref_no_numbers(obj);
+ put_int16(i, ep);
+ ep += 2;
+
+ sysname = external_ref_node_name(obj);
+ creation = external_ref_creation(obj);
+ goto ref_common;
- erts_magic_ref_save_bin(obj);
+ case REF_DEF:
*ep++ = NEWER_REFERENCE_EXT;
- i = ref_no_numbers(obj);
- put_int16(i, ep);
- ep += 2;
- ep = enc_atom(acmp, sysname, ep, dflags);
+
+ erts_magic_ref_save_bin(obj);
+
+ ref_num = internal_ref_numbers(obj);
+ i = internal_ref_no_numbers(obj);
+ put_int16(i, ep);
+ ep += 2;
+
+ if (dflags & (DFLAG_ETS_COMPRESSED|DFLAG_LOCAL_EXT)) {
+ *ep++ = NIL_EXT; /* INTERNAL_LOCAL_NODE */
+ creation = 0;
+ }
+ else {
+ sysname = internal_ref_node_name(obj);
+ creation = internal_ref_creation(obj);
+ ref_common:
+ ep = enc_atom(acmp, sysname, ep, dflags);
+ }
+
put_int32(creation, ep);
ep += 4;
- ref_num = ref_numbers(obj);
for (j = 0; j < i; j++) {
put_int32(ref_num[j], ep);
ep += 4;
}
break;
}
- case PORT_DEF:
case EXTERNAL_PORT_DEF: {
- Eterm sysname = (((dflags & DFLAG_ETS_COMPRESSED) && is_internal_port(obj))
- ? INTERNAL_LOCAL_SYSNAME : port_node_name(obj));
- Uint32 creation = port_creation(obj);
- byte *tagp = ep++;
- Uint64 num;
-
- ep = enc_atom(acmp, sysname, ep, dflags);
- num = port_number(obj);
- if (num > ERTS_MAX_V3_PORT_NUMBER) {
- *tagp = V4_PORT_EXT;
- put_int64(num, ep);
- ep += 8;
- }
- else {
- *tagp = NEW_PORT_EXT;
- put_int32(num, ep);
- ep += 4;
- }
+ Eterm sysname;
+ Uint64 number;
+ Uint32 creation;
+
+ *ep++ = V4_PORT_EXT;
+ number = external_port_number(obj);
+ sysname = external_port_node_name(obj);
+ creation = external_port_creation(obj);
+
+ goto port_common;
+
+ case PORT_DEF:
+
+ *ep++ = V4_PORT_EXT;
+ number = internal_port_number(obj);
+ if (dflags & (DFLAG_ETS_COMPRESSED|DFLAG_LOCAL_EXT)) {
+ *ep++ = NIL_EXT; /* INTERNAL_LOCAL_NODE */
+ creation = 0;
+ }
+ else {
+ sysname = internal_port_node_name(obj);
+ creation = internal_port_creation(obj);
+ port_common:
+ ep = enc_atom(acmp, sysname, ep, dflags);
+ }
+
+ put_int64(number, ep);
+ ep += 8;
put_int32(creation, ep);
ep += 4;
break;
@@ -3743,6 +3912,68 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
if (use_iov)
store_in_vec(ctx, ep, NULL, THE_NON_VALUE, NULL, 0);
}
+ if (dflags & DFLAG_LOCAL_EXT) {
+ int done;
+ Uint32 hash;
+ Uint chunk_len;
+
+ if (use_iov) {
+ ASSERT(ctx);
+ erts_iov_block_hash_init(&ctx->lext_state.iov_block,
+ &ctx->iov[ctx->lext_vlen],
+ ctx->vlen - ctx->lext_vlen,
+ local_node_hash);
+
+ continue_make_lext_hash_iov:
+ /* Do 128 bytes per reduction... */
+ chunk_len = (Uint) r*128;
+
+ done = erts_iov_block_hash(&hash, &chunk_len,
+ &ctx->lext_state.iov_block);
+ }
+ else {
+ ErtsBlockHashState lext_state_buf, *lext_state;
+ byte *ep_start = lext_hash + 4 /* 32 bit hash value */;
+ Sint len = ep - ep_start;
+
+ ASSERT(len >= 0);
+
+ lext_state = ctx ? &ctx->lext_state.block : &lext_state_buf;
+
+ erts_block_hash_init(lext_state, ep_start, len, local_node_hash);
+
+ if (!ctx) {
+ /* Do it all at once... */
+ chunk_len = ERTS_UINT_MAX;
+ }
+ else {
+ continue_make_lext_hash_bin:
+ lext_state = &ctx->lext_state.block;
+ /* Do 128 bytes per reduction... */
+ chunk_len = (Uint) r*128;
+ }
+
+ done = erts_block_hash(&hash, &chunk_len, lext_state);
+ }
+
+ if (!ctx) {
+ ASSERT(done);
+ }
+ else {
+ if (!done) {
+ /* yield; more work calculating hash... */
+ ctx->ep = ep;
+ ctx->continue_make_lext_hash = !0;
+ *reds = 0;
+ return -1;
+ }
+ r -= chunk_len/128;
+ *reds = r;
+ }
+
+ put_int32(hash, lext_hash);
+ }
+
*res = ep;
return 0;
}
@@ -3926,7 +4157,7 @@ dec_term(ErtsDistExternal *edep,
{
#define PSTACK_TYPE struct dec_term_map
PSTACK_DECLARE(map_array, 10);
- int n;
+ int n, internal_nc = ets_decode;
ErtsAtomEncoding char_enc;
register Eterm* hp; /* Please don't take the address of hp */
Eterm* next;
@@ -3940,6 +4171,7 @@ dec_term(ErtsDistExternal *edep,
next = ctx->u.dc.next;
ep = ctx->u.dc.ep;
factory = &ctx->u.dc.factory;
+ internal_nc = ctx->u.dc.internal_nc;
if (ctx->state != B2TDecode) {
int n_limit = reds;
@@ -4031,6 +4263,8 @@ dec_term(ErtsDistExternal *edep,
objp = next;
next = (Eterm *) *objp;
+ continue_this_obj:
+
switch (*ep++) {
case INTEGER_EXT:
{
@@ -4274,7 +4508,7 @@ dec_term_atom_common:
case PID_EXT:
case NEW_PID_EXT:
factory->hp = hp;
- ep = dec_pid(edep, factory, ep, objp, ep[-1]);
+ ep = dec_pid(edep, factory, ep, objp, ep[-1], internal_nc);
hp = factory->hp;
if (ep == NULL) {
goto error;
@@ -4290,7 +4524,7 @@ dec_term_atom_common:
Uint32 cre;
byte tag = ep[-1];
- if ((ep = dec_atom(edep, ep, &sysname)) == NULL) {
+ if ((ep = dec_atom(edep, ep, &sysname, internal_nc)) == NULL) {
goto error;
}
if (tag == V4_PORT_EXT) {
@@ -4350,7 +4584,7 @@ dec_term_atom_common:
ref_words = 1;
- if ((ep = dec_atom(edep, ep, &sysname)) == NULL)
+ if ((ep = dec_atom(edep, ep, &sysname, internal_nc)) == NULL)
goto error;
if ((r0 = get_int32(ep)) >= MAX_REFERENCE )
goto error;
@@ -4367,7 +4601,7 @@ dec_term_atom_common:
ref_words = get_int16(ep);
ep += 2;
- if ((ep = dec_atom(edep, ep, &sysname)) == NULL)
+ if ((ep = dec_atom(edep, ep, &sysname, internal_nc)) == NULL)
goto error;
cre = get_int8(ep);
@@ -4385,7 +4619,7 @@ dec_term_atom_common:
ref_words = get_int16(ep);
ep += 2;
- if ((ep = dec_atom(edep, ep, &sysname)) == NULL)
+ if ((ep = dec_atom(edep, ep, &sysname, internal_nc)) == NULL)
goto error;
cre = get_int32(ep);
@@ -4623,10 +4857,10 @@ dec_term_atom_common:
Eterm temp;
Sint arity;
- if ((ep = dec_atom(edep, ep, &mod)) == NULL) {
+ if ((ep = dec_atom(edep, ep, &mod, 0)) == NULL) {
goto error;
}
- if ((ep = dec_atom(edep, ep, &name)) == NULL) {
+ if ((ep = dec_atom(edep, ep, &name, 0)) == NULL) {
goto error;
}
factory->hp = hp;
@@ -4741,7 +4975,7 @@ dec_term_atom_common:
*objp = make_fun(funp);
/* Module */
- if ((ep = dec_atom(edep, ep, &module)) == NULL) {
+ if ((ep = dec_atom(edep, ep, &module, 0)) == NULL) {
goto error;
}
factory->hp = hp;
@@ -4854,6 +5088,13 @@ dec_term_atom_common:
break;
}
+ case LOCAL_EXT:
+ internal_nc = !0;
+ if (ctx)
+ ctx->u.dc.internal_nc = !0;
+ ep += 4; /* 32-bit hash (verified in decoded_size()) */
+ goto continue_this_obj;
+
default:
goto error;
}
@@ -4954,23 +5195,88 @@ error_map_fixup:
return NULL;
}
-/* returns the number of bytes needed to encode an object
- to a sequence of bytes
- N.B. That this must agree with to_external2() above!!!
- (except for cached atoms) */
-static Uint encode_size_struct2(ErtsAtomCacheMap *acmp,
- Eterm obj,
- Uint64 dflags) {
- Uint size = 0;
- ErtsExtSzRes res = encode_size_struct_int(NULL, acmp, obj,
- dflags, NULL,
- &size);
- /*
- * encode_size_struct2() only allowed when
- * we know the result will always be OK!
- */
- ASSERT(res == ERTS_EXT_SZ_OK); (void) res;
- return (Uint) size;
+static Uint
+encode_atom_size(ErtsAtomCacheMap *acmp, Eterm atom, Uint64 dflags)
+{
+ ASSERT(is_atom(atom));
+ if (dflags & DFLAG_ETS_COMPRESSED) {
+ if (atom_val(atom) >= (1<<16)) {
+ return (Uint) 1 + 3;
+ }
+ else {
+ return (Uint) 1 + 2;
+ }
+ }
+ else {
+ Atom *a = atom_tab(atom_val(atom));
+ int alen;
+ Uint result;
+ if ((dflags & DFLAG_UTF8_ATOMS) || a->latin1_chars < 0) {
+ alen = a->len;
+ result = (Uint) 1 + 1 + alen;
+ if (alen > 255) {
+ result++; /* ATOM_UTF8_EXT (not small) */
+ }
+ }
+ else {
+ alen = a->latin1_chars;
+ result = (Uint) 1 + 1 + alen;
+ if (alen > 255 || !(dflags & DFLAG_SMALL_ATOM_TAGS))
+ result++; /* ATOM_EXT (not small) */
+ }
+ insert_acache_map(acmp, atom, dflags);
+ return result;
+ }
+}
+
+static Uint
+encode_internal_pid_size(ErtsAtomCacheMap *acmp, Eterm pid, Uint64 dflags)
+{
+ int nlen;
+ ASSERT(is_internal_pid(pid));
+ nlen = ((dflags & (DFLAG_ETS_COMPRESSED|DFLAG_LOCAL_EXT))
+ ? 1
+ : encode_atom_size(acmp, internal_pid_node_name(pid), dflags));
+ return (Uint) 1 + nlen + 4 + 4 + 4;
+}
+
+static Uint
+encode_external_pid_size(ErtsAtomCacheMap *acmp, Eterm pid, Uint64 dflags)
+{
+ int nlen;
+ ASSERT(is_external_pid(pid));
+ nlen = encode_atom_size(acmp, external_pid_node_name(pid), dflags);
+ return (Uint) 1 + nlen + 4 + 4 + 4;
+}
+
+static Uint
+encode_pid_size(ErtsAtomCacheMap *acmp, Eterm pid, Uint64 dflags)
+{
+ if (is_internal_pid(pid))
+ return encode_internal_pid_size(acmp, pid, dflags);
+ ASSERT(is_external_pid(pid));
+ return encode_external_pid_size(acmp, pid, dflags);
+}
+
+static Uint
+encode_small_size(ErtsAtomCacheMap *acmp, Eterm pid, Uint64 dflags)
+{
+ Sint val = signed_val(pid);
+ Uint result;
+
+ if ((Uint)val < 256)
+ result = (Uint) 1 + 1; /* SMALL_INTEGER_EXT */
+ else if (sizeof(Sint) == 4 || IS_SSMALL32(val))
+ result = (Uint) 1 + 4; /* INTEGER_EXT */
+ else {
+ int i;
+ DeclareTmpHeapNoproc(tmp_big,2);
+ UseTmpHeapNoproc(2);
+ i = big_bytes(small_to_big(val, tmp_big));
+ result = (Uint) 1 + 1 + 1 + i; /* SMALL_BIG_EXT */
+ UnUseTmpHeapNoproc(2);
+ }
+ return result;
}
static ErtsExtSzRes
@@ -4983,14 +5289,24 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
Sint r = 0;
int vlen = -1;
- if (ctx) {
+ if (!ctx) {
+ if (dflags & DFLAG_LOCAL_EXT)
+ result += 5;
+ }
+ else {
WSTACK_CHANGE_ALLOCATOR(s, ERTS_ALC_T_SAVED_ESTACK);
r = *reds;
vlen = ctx->vlen;
- if (!ctx->wstack.wstart)
+ if (!ctx->wstack.wstart) {
ctx->last_result = result;
+ if (dflags & DFLAG_LOCAL_EXT) {
+ result += 5;
+ if (vlen >= 0)
+ vlen++;
+ }
+ }
else { /* restore saved stack */
WSTACK_RESTORE(s, &ctx->wstack);
result = ctx->result;
@@ -5019,49 +5335,10 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result++;
break;
case ATOM_DEF:
- if (dflags & DFLAG_ETS_COMPRESSED) {
- if (atom_val(obj) >= (1<<16)) {
- result += 1 + 3;
- }
- else {
- result += 1 + 2;
- }
- }
- else {
- Atom *a = atom_tab(atom_val(obj));
- int alen;
- if ((dflags & DFLAG_UTF8_ATOMS) || a->latin1_chars < 0) {
- alen = a->len;
- result += 1 + 1 + alen;
- if (alen > 255) {
- result++; /* ATOM_UTF8_EXT (not small) */
- }
- }
- else {
- alen = a->latin1_chars;
- result += 1 + 1 + alen;
- if (alen > 255 || !(dflags & DFLAG_SMALL_ATOM_TAGS))
- result++; /* ATOM_EXT (not small) */
- }
- insert_acache_map(acmp, obj, dflags);
- }
+ result += encode_atom_size(acmp, obj, dflags);
break;
case SMALL_DEF:
- {
- Sint val = signed_val(obj);
-
- if ((Uint)val < 256)
- result += 1 + 1; /* SMALL_INTEGER_EXT */
- else if (sizeof(Sint) == 4 || IS_SSMALL32(val))
- result += 1 + 4; /* INTEGER_EXT */
- else {
- DeclareTmpHeapNoproc(tmp_big,2);
- UseTmpHeapNoproc(2);
- i = big_bytes(small_to_big(val, tmp_big));
- result += 1 + 1 + 1 + i; /* SMALL_BIG_EXT */
- UnUseTmpHeapNoproc(2);
- }
- }
+ result += encode_small_size(acmp, obj, dflags);
break;
case BIG_DEF:
i = big_bytes(obj);
@@ -5073,22 +5350,47 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 1 + 4 + 1 + i; /* tag,size,sign,digits */
break;
case EXTERNAL_PID_DEF:
+ result += encode_external_pid_size(acmp, obj, dflags);
+ break;
case PID_DEF:
- result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) +
- 4 + 4 + 4);
+ result += encode_internal_pid_size(acmp, obj, dflags);
break;
- case EXTERNAL_REF_DEF:
- case REF_DEF:
- i = ref_no_numbers(obj);
- result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) +
- 4 + 4*i);
+ case EXTERNAL_REF_DEF: {
+ int nlen = encode_atom_size(acmp,
+ external_ref_node_name(obj),
+ dflags);
+ i = external_ref_no_numbers(obj);
+ result += (1 + 2 + nlen + 4 + 4*i);
break;
- case EXTERNAL_PORT_DEF:
+ }
+ case REF_DEF: {
+ int nlen;
+ i = internal_ref_no_numbers(obj);
+ if (dflags & (DFLAG_ETS_COMPRESSED|DFLAG_LOCAL_EXT)) {
+ nlen = 1;
+ }
+ else {
+ nlen = encode_atom_size(acmp,
+ internal_ref_node_name(obj),
+ dflags);
+ }
+ result += (1 + 2 + nlen + 4 + 4*i);
+ break;
+ }
+ case EXTERNAL_PORT_DEF: {
+ int nlen = encode_atom_size(acmp,
+ external_port_node_name(obj),
+ dflags);
+ result += (1 + nlen + 8 + 4);
+ break;
+ }
case PORT_DEF: {
- Uint64 num = port_number(obj);
- result += (num > ERTS_MAX_V3_PORT_NUMBER) ? 8 : 4;
- result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags)
- /* num */ + 4);
+ int nlen = ((dflags & (DFLAG_ETS_COMPRESSED|DFLAG_LOCAL_EXT))
+ ? 1
+ : encode_atom_size(acmp,
+ internal_port_node_name(obj),
+ dflags));
+ result += (1 + nlen + 8 + 4);
break;
}
case LIST_DEF: {
@@ -5275,8 +5577,8 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
if (is_local_fun(funp)) {
result += 20+1+1+4; /* New ID + Tag */
result += 4; /* Length field (number of free variables */
- result += encode_size_struct2(acmp, funp->creator, dflags);
- result += encode_size_struct2(acmp, funp->entry.fun->module, dflags);
+ result += encode_pid_size(acmp, funp->creator, dflags);
+ result += encode_atom_size(acmp, funp->entry.fun->module, dflags);
result += 2 * (1+4); /* Index, Uniq */
if (funp->num_free > 1) {
WSTACK_PUSH2(s, (UWord) (funp->env + 1),
@@ -5292,9 +5594,9 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
ASSERT(is_external_fun(funp) && funp->next == NULL);
result += 1;
- result += encode_size_struct2(acmp, ep->info.mfa.module, dflags);
- result += encode_size_struct2(acmp, ep->info.mfa.function, dflags);
- result += encode_size_struct2(acmp, make_small(ep->info.mfa.arity), dflags);
+ result += encode_atom_size(acmp, ep->info.mfa.module, dflags);
+ result += encode_atom_size(acmp, ep->info.mfa.function, dflags);
+ result += encode_small_size(acmp, make_small(ep->info.mfa.arity), dflags);
}
break;
}
@@ -5350,8 +5652,6 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
return ERTS_EXT_SZ_OK;
}
-
-
static Sint
decoded_size(const byte *ep, const byte* endp, int internal_tags, B2TContext* ctx)
{
@@ -5359,6 +5659,8 @@ decoded_size(const byte *ep, const byte* endp, int internal_tags, B2TContext* ct
int atom_extra_skip;
Uint n;
SWord reds;
+ const byte *lext_hash;
+ Uint32 lext_term_end;
/* Keep track of the current number of sub terms remaining to be decoded.
*
@@ -5371,24 +5673,6 @@ decoded_size(const byte *ep, const byte* endp, int internal_tags, B2TContext* ct
*/
Uint32 terms;
- if (ctx) {
- reds = ctx->reds;
- if (ctx->u.sc.ep) {
- heap_size = ctx->u.sc.heap_size;
- terms = ctx->u.sc.terms;
- ep = ctx->u.sc.ep;
- atom_extra_skip = ctx->u.sc.atom_extra_skip;
- goto init_done;
- }
- }
- else
- ERTS_UNDEF(reds, 0);
-
- heap_size = 0;
- terms = 1;
- atom_extra_skip = 0;
-init_done:
-
#define SKIP(sz) \
do { \
if ((sz) <= endp-ep) { \
@@ -5418,6 +5702,38 @@ init_done:
if (terms < before) goto error; \
} while (0)
+
+ if (ctx) {
+ reds = ctx->reds;
+ if (ctx->u.sc.ep) {
+ ep = ctx->u.sc.ep;
+ atom_extra_skip = ctx->u.sc.atom_extra_skip;
+ heap_size = ctx->u.sc.heap_size;
+ lext_hash = ctx->u.sc.lext_hash;
+ lext_term_end = ctx->u.sc.lext_term_end;
+ terms = ctx->u.sc.terms;
+ if (terms == lext_term_end) {
+ ASSERT(lext_hash);
+ goto continue_check_lext;
+ }
+ goto init_done;
+ }
+ }
+ else
+ ERTS_UNDEF(reds, 0);
+
+ heap_size = 0;
+ terms = 1;
+ atom_extra_skip = 0;
+ /*
+ * lext_hash != NULL and local_term_end != ~0 when decoding local external
+ * term format...
+ */
+ lext_hash = NULL;
+ lext_term_end = ~0;
+
+init_done:
+
ASSERT(terms > 0);
do {
int tag;
@@ -5546,6 +5862,18 @@ init_done:
terms++;
break;
case NIL_EXT:
+ if (atom_extra_skip) {
+ /*
+ * atom_extra_skip != 0 should only appear due to local encoding,
+ * or compressed ets encoding, of a node name in internal
+ * pids/ports/refs. If not currently inside a local encoding,
+ * this is an error...
+ */
+ if (!lext_hash && !internal_tags)
+ goto error;
+ SKIP(atom_extra_skip);
+ atom_extra_skip = 0;
+ }
break;
case LIST_EXT:
CHKSIZE(4);
@@ -5692,22 +6020,87 @@ init_done:
SKIP(2+sizeof(ProcBin));
heap_size += PROC_BIN_SIZE + ERL_SUB_BIN_SIZE;
break;
+ CHKSIZE(1);
+ case LOCAL_EXT:
+ /*
+ * Currently the hash is 4 bytes large...
+ */
+ CHKSIZE(4);
+ lext_hash = ep;
+ lext_term_end = terms - 1;
+ terms++;
+ ep += 4;
+ break;
default:
goto error;
}
terms--;
+ if (terms == lext_term_end) {
+ ErtsBlockHashState lext_state_buf, *lext_state;
+ const byte *ep_start = lext_hash + 4 /* 32 bit hash value */;
+ Sint len = ep - ep_start;
+ Uint chunk_len;
+ Uint32 hash;
+
+ ASSERT(lext_hash);
+
+ if (len <= 0) {
+ /* no terms */
+ goto error;
+ }
+
+ lext_state = ctx ? &ctx->u.sc.lext_state : &lext_state_buf;
+
+ erts_block_hash_init(lext_state, ep_start, len, local_node_hash);
+
+ if (!ctx) {
+ /* Do it all at once... */
+ chunk_len = ERTS_UINT_MAX;
+ }
+ else {
+ continue_check_lext:
+ lext_state = &ctx->u.sc.lext_state;
+ /* 'reds' has been scaled up to "bytes per reds"... */
+ chunk_len = (Uint) reds;
+ }
+
+ if (!erts_block_hash(&hash, &chunk_len, lext_state)) {
+ /* yield; more work calculating hash... */
+ ASSERT(ctx);
+ reds = 0;
+ }
+ else {
+ reds -= chunk_len;
+ if (hash != get_int32(lext_hash)) {
+ /*
+ * Hash presented in external format did not match the
+ * calculated hash...
+ */
+ goto error;
+ }
+ lext_hash = NULL;
+ lext_term_end = ~0;
+ }
+
+ }
+
+ ASSERT(terms != ~0);
+
if (ctx && --reds <= 0 && terms != 0) {
ctx->u.sc.heap_size = heap_size;
ctx->u.sc.terms = terms;
ctx->u.sc.ep = ep;
ctx->u.sc.atom_extra_skip = atom_extra_skip;
+ ctx->u.sc.lext_hash = lext_hash;
+ ctx->u.sc.lext_term_end = lext_term_end;
ctx->reds = 0;
return 0;
}
} while (terms != 0);
if (terms == 0) {
+
if (ctx) {
ctx->state = B2TDecodeInit;
ctx->reds = reds;
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index 7a59d02102..2d2a02b23e 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -57,6 +57,7 @@
#define ATOM_UTF8_EXT 'v'
#define SMALL_ATOM_UTF8_EXT 'w'
#define V4_PORT_EXT 'x'
+#define LOCAL_EXT 'y'
#define DIST_HEADER 'D'
#define DIST_FRAG_HEADER 'E'
@@ -187,7 +188,7 @@ int erts_encode_dist_ext(Eterm, byte **, Uint64, ErtsAtomCacheMap *,
struct TTBEncodeContext_ *, Uint *,
Sint *);
ErtsExtSzRes erts_encode_ext_size(Eterm, Uint *szp);
-ErtsExtSzRes erts_encode_ext_size_2(Eterm, unsigned, Uint *szp);
+ErtsExtSzRes erts_encode_ext_size_2(Eterm, Uint64, Uint *szp);
Uint erts_encode_ext_size_ets(Eterm);
void erts_encode_ext(Eterm, byte **);
byte* erts_encode_ext_ets(Eterm, byte *, struct erl_off_heap_header** ext_off_heap);
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index 52d054a212..11f34b5444 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1466,6 +1466,7 @@ Eterm erts_debug_persistent_term_xtra_info(Process* c_p);
/* external.c */
void erts_init_external(void);
+void erts_late_init_external(void);
/* erl_map.c */
void erts_init_map(void);
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index b57cfd6952..821bc9f2c2 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -847,6 +847,7 @@ int sys_double_to_chars(double, char*, size_t);
int sys_double_to_chars_ext(double, char*, size_t, size_t);
int sys_double_to_chars_fast(double, char*, int, int, int);
void sys_get_pid(char *, size_t);
+int sys_get_hostname(char *buf, size_t size);
/* erl_drv_get/putenv have been implicitly 8-bit for so long that we can't
* change them without breaking things on Windows. Their return values are
diff --git a/erts/emulator/sys/unix/sys.c b/erts/emulator/sys/unix/sys.c
index 7ff8425d52..26959a9716 100644
--- a/erts/emulator/sys/unix/sys.c
+++ b/erts/emulator/sys/unix/sys.c
@@ -814,6 +814,10 @@ void sys_get_pid(char *buffer, size_t buffer_size){
erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
+int sys_get_hostname(char *buf, size_t size)
+{
+ return gethostname(buf, size);
+}
void sys_init_io(void) { }
void erts_sys_alloc_init(void) { }
diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c
index 79f003c7c9..1fc8801e56 100644
--- a/erts/emulator/sys/win32/sys.c
+++ b/erts/emulator/sys/win32/sys.c
@@ -35,6 +35,10 @@
#include "erl_cpu_topology.h"
#include <malloc.h>
+#if defined(__WIN32__) && !defined(WINDOWS_H_INCLUDES_WINSOCK2_H)
+#include <winsock2.h>
+#endif
+
void erts_sys_init_float(void);
void erl_start(int, char**);
@@ -2767,6 +2771,11 @@ void sys_get_pid(char *buffer, size_t buffer_size){
erts_snprintf(buffer, buffer_size, "%lu",(unsigned long) p);
}
+int sys_get_hostname(char *buf, size_t size)
+{
+ return gethostname(buf, size);
+}
+
void
sys_init_io(void)
{
diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl
index b8683385cf..cf63cce3d7 100644
--- a/erts/emulator/test/binary_SUITE.erl
+++ b/erts/emulator/test/binary_SUITE.erl
@@ -77,7 +77,8 @@
error_after_yield/1, cmp_old_impl/1,
t2b_system_limit/1,
term_to_iovec/1,
- is_binary_test/1]).
+ is_binary_test/1,
+ local_ext/1]).
%% Internal exports.
-export([sleeper/0,trapping_loop/4]).
@@ -106,7 +107,8 @@ all() ->
bit_sized_binary_sizes, otp_6817, otp_8117, deep,
robustness, otp_8180, trapping, large,
error_after_yield, cmp_old_impl,
- is_binary_test].
+ is_binary_test,
+ local_ext].
groups() ->
[
@@ -2348,3 +2350,177 @@ list2bitstrlist([X0, X1, X2, X3, X4, X5 | Xs], Acc) when is_integer(X0), 0 =< X0
list2bitstrlist(Xs, NewAcc);
list2bitstrlist([X | Xs], Acc) ->
list2bitstrlist(Xs, [Acc,X]).
+
+local_ext(Config) when is_list(Config) ->
+ SDrv = send_term_local_drv,
+ CDrv = call_local_drv,
+ DataDir = proplists:get_value(data_dir, Config),
+ PrivDir = proplists:get_value(priv_dir, Config),
+ FileName = filename:join(PrivDir, "local_ext.data"),
+ Args = ["-setcookie", atom_to_list(erlang:get_cookie()),
+ "-pa", filename:dirname(code:which(?MODULE))],
+ {ok, Peer1, _} = peer:start_link(#{connection => 0, args => Args}),
+ {ok, Peer2, _} = peer:start_link(#{connection => 0, args => Args}),
+ LongNames = net_kernel:longnames(),
+ DynStartOpts = #{name_domain => if LongNames -> longnames;
+ true -> shortnames
+ end},
+ ExternalPid = self(),
+ ExternalRef = make_ref(),
+ ExternalPort = hd(erlang:ports()),
+ EncDecLocal = fun () ->
+ erl_ddll:start(),
+ ok = erl_ddll:load_driver(DataDir, SDrv),
+ SPort = open_port({spawn, SDrv}, []),
+ ok = erl_ddll:load_driver(DataDir, CDrv),
+ CPort = open_port({spawn, CDrv}, []),
+ false = erlang:is_alive(),
+ nonode@nohost = node(),
+ LocalPid = self(),
+ LocalRef = make_ref(),
+ LocalPort = hd(erlang:ports()),
+ Bin1 = <<4711:800>>,
+ Bin2 = <<4711:703>>,
+ Bin3 = <<4711:600>>,
+ Terms = [
+ LocalPid,
+ ExternalPid,
+ LocalRef,
+ ExternalRef,
+ LocalPort,
+ ExternalPort,
+ [LocalPid, Bin1, ExternalPid, LocalRef, Bin2,
+ ExternalRef, Bin3, Bin2, LocalPort,
+ ExternalPort],
+ "hej",
+ [],
+ {processes(), Bin3, erlang:ports(), Bin3},
+ #{pid => LocalPid, ref => LocalRef, port => LocalPort}
+ ],
+ {ok, FD} = file:open(FileName, [write]),
+ ETs = lists:map(fun (Term) ->
+ {enc_local(FD, Term), Term}
+ end, Terms),
+ ok = file:close(FD),
+ CheckET = fun ({LExt, Term}) ->
+ Term = binary_to_term(LExt),
+ SPort ! {self(), {command, LExt}},
+ receive
+ {SPort, Reply} ->
+ Term = Reply
+ end
+ end,
+ lists:foreach(CheckET, ETs),
+ call_local_success(CPort, ETs),
+ NodeName = peer:random_name(),
+ {ok, _} = net_kernel:start(list_to_atom(NodeName),
+ DynStartOpts),
+ true = erlang:is_alive(),
+ true = nonode@nohost /= node(),
+ lists:foreach(CheckET, ETs),
+ call_local_success(CPort, ETs),
+ ok = net_kernel:stop(),
+ false = erlang:is_alive(),
+ nonode@nohost = node(),
+ lists:foreach(CheckET, ETs),
+ call_local_success(CPort, ETs),
+ {ok, ExtList} = file:consult(FileName),
+ lists:foreach(fun (Ext) when is_binary(Ext) ->
+ _ = binary_to_term(Ext),
+ SPort ! {self(), {command, Ext}},
+ receive
+ {SPort, "bad_term_error"} ->
+ error(bad_term_error);
+ {SPort, _} ->
+ ok
+ end
+ end,
+ ExtList),
+ true = port_close(SPort),
+ true = port_close(CPort),
+ ok
+ end,
+ ok = peer:call(Peer1, erlang, apply, [EncDecLocal, []]),
+ DecOthersLocal = fun () ->
+ %% Verify that decoding of the terms encoded
+ %% on local external format by the other runtime
+ %% system instance fails on this runtime system
+ %% instance...
+ erl_ddll:start(),
+ ok = erl_ddll:load_driver(DataDir, SDrv),
+ SPort = open_port({spawn, SDrv}, []),
+ ok = erl_ddll:load_driver(DataDir, CDrv),
+ CPort = open_port({spawn, CDrv}, []),
+ false = erlang:is_alive(),
+ nonode@nohost = node(),
+ {ok, ExtList} = file:consult(FileName),
+ lists:foreach(fun (Ext) when is_binary(Ext) ->
+ try
+ Term = binary_to_term(Ext),
+ error({successful_decode, Term})
+ catch
+ error:badarg ->
+ ok
+ end,
+ SPort ! {self(), {command, Ext}},
+ receive
+ {SPort, Reply} ->
+ "bad_term_error" = Reply
+ end
+ end,
+ ExtList),
+ call_local_fail(CPort, ExtList),
+ true = port_close(SPort),
+ true = port_close(CPort),
+ ok
+ end,
+ ok = peer:call(Peer2, erlang, apply, [DecOthersLocal, []]),
+ peer:stop(Peer1),
+ peer:stop(Peer2),
+ ok.
+
+enc_local(FD, Term) ->
+ Ext = term_to_binary(Term, [local]),
+ Ext = iolist_to_binary(term_to_iovec(Term, [local])),
+ Term = binary_to_term(Ext),
+ io:format(FD, "~p.~n", [Ext]),
+ Ext.
+
+call_local_success(Port, []) ->
+ ok;
+call_local_success(Port, [{Lext1, T1}]) ->
+ Me = self(),
+ Ref = make_ref(),
+ Term = {term_to_binary(Me), Lext1, term_to_binary(Ref)},
+ {call_result, Me, 4711, T1, 17, Ref, "end_of_data"} = erlang:port_call(Port, 0, Term),
+ ok;
+call_local_success(Port, [{Lext1, T1}, {Lext3, T3} | Rest]) ->
+ Me = self(),
+ Term = {Lext1, term_to_binary(Me), Lext3},
+ {call_result, T1, 4711, Me, 17, T3, "end_of_data"} = erlang:port_call(Port, 0, Term),
+ call_local_success(Port, Rest).
+
+call_local_fail(Port, []) ->
+ ok;
+call_local_fail(Port, [Lext1]) ->
+ Me = self(),
+ Ref = make_ref(),
+ Term = {term_to_binary(Me), Lext1, term_to_binary(Ref)},
+ try
+ erlang:port_call(Port, 0, Term),
+ error(unexpected_port_call_success)
+ catch
+ error:badarg ->
+ ok
+ end;
+call_local_fail(Port, [Lext1, Lext3 | Rest]) ->
+ Me = self(),
+ Term = {Lext1, term_to_binary(Me), Lext3},
+ try
+ erlang:port_call(Port, 0, Term),
+ error(unexpected_port_call_success)
+ catch
+ error:badarg ->
+ ok
+ end,
+ call_local_fail(Port, Rest).
diff --git a/erts/emulator/test/binary_SUITE_data/Makefile.src b/erts/emulator/test/binary_SUITE_data/Makefile.src
new file mode 100644
index 0000000000..541dd6c1ad
--- /dev/null
+++ b/erts/emulator/test/binary_SUITE_data/Makefile.src
@@ -0,0 +1,33 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2023. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# %CopyrightEnd%
+#
+
+include @erl_interface_mk_include@
+
+CC = @CC@
+LD = @LD@
+CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@
+CROSSLDFLAGS = @CROSSLDFLAGS@
+
+SHLIB_EXTRA_CFLAGS = @EI_CFLAGS@ -I@erl_interface_include@
+SHLIB_EXTRA_LDLIBS = @erl_interface_eilib@
+
+all: send_term_local_drv@dll@ call_local_drv@dll@
+
+@SHLIB_RULES@
diff --git a/erts/emulator/test/binary_SUITE_data/call_local_drv.c b/erts/emulator/test/binary_SUITE_data/call_local_drv.c
new file mode 100644
index 0000000000..8db6c1c772
--- /dev/null
+++ b/erts/emulator/test/binary_SUITE_data/call_local_drv.c
@@ -0,0 +1,204 @@
+/* ``Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+ * Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+ * AB. All Rights Reserved.''
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "erl_driver.h"
+#include "ei.h"
+
+static ErlDrvSSizeT call(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen,
+ unsigned int *flags);
+
+static ErlDrvEntry call_local_drv_entry = {
+ NULL /* init */,
+ NULL /* start */,
+ NULL /* stop */,
+ NULL /* output */,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ "call_local_drv",
+ NULL /* finish */,
+ NULL /* handle */,
+ NULL /* control */,
+ NULL /* timeout */,
+ NULL /* outputv */,
+ NULL /* ready_async */,
+ NULL /* flush */,
+ call,
+ NULL /* event */,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL /* handle2 */,
+ NULL /* handle_monitor */
+};
+
+DRIVER_INIT(call_local_drv)
+{
+ return &call_local_drv_entry;
+}
+
+
+static ErlDrvSSizeT call(ErlDrvData drv_data,
+ unsigned int command,
+ char *buf, ErlDrvSizeT len,
+ char **rbuf, ErlDrvSizeT rlen,
+ unsigned int *flags)
+{
+ ei_x_buff xbuf;
+ void *bin1 = NULL, *bin2 = NULL, *bin3 = NULL;
+ char *lext1, *lext2, *lext3;
+ int vsn, arity, type, ix, lix, res, err, size;
+ long size1, size2, size3;
+ ErlDrvSSizeT ret_size = (ErlDrvSSizeT) ERL_DRV_ERROR_GENERAL;
+
+ xbuf.buff = NULL;
+
+ ei_init();
+
+ ix = 0;
+ res = ei_decode_version(buf, &ix, &vsn);
+ if (res != 0 || vsn != 131)
+ goto error;
+
+ res = ei_decode_tuple_header(buf, &ix, &arity);
+ if (res != 0)
+ goto error;
+
+ /* External term 1 */
+ res = ei_get_type(buf, &ix, &type, &size);
+ if (res != 0 && type != ERL_BINARY_EXT)
+ goto error;
+
+ size1 = size;
+ bin1 = driver_alloc(size1);
+
+ res = ei_decode_binary(buf, &ix, bin1, &size1);
+ if (res != 0 && type != ERL_BINARY_EXT)
+ goto error;
+
+ lext1 = bin1;
+ lix = 0;
+ res = ei_decode_version(lext1, &lix, &vsn);
+ if (res != 0 || vsn != 131)
+ goto error;
+ lext1 += lix;
+ size1 -= lix;
+
+ /* External term 2 */
+ res = ei_get_type(buf, &ix, &type, &size);
+ if (res != 0 && type != ERL_BINARY_EXT)
+ goto error;
+
+ size2 = size;
+ bin2 = driver_alloc(size2);
+
+ res = ei_decode_binary(buf, &ix, bin2, &size2);
+ if (res != 0 && type != ERL_BINARY_EXT)
+ goto error;
+
+ lext2 = bin2;
+ lix = 0;
+ res = ei_decode_version(lext2, &lix, &vsn);
+ if (res != 0 || vsn != 131)
+ goto error;
+ lext2 += lix;
+ size2 -= lix;
+
+ /* External term 3 */
+ res = ei_get_type(buf, &ix, &type, &size);
+ if (res != 0 && type != ERL_BINARY_EXT)
+ goto error;
+
+ size3 = size;
+ bin3 = driver_alloc(size3);
+
+ res = ei_decode_binary(buf, &ix, bin3, &size3);
+ if (res != 0 && type != ERL_BINARY_EXT)
+ goto error;
+
+ lext3 = bin3;
+ lix = 0;
+ res = ei_decode_version(lext3, &lix, &vsn);
+ if (res != 0 || vsn != 131)
+ goto error;
+ lext3 += lix;
+ size3 -= lix;
+
+ /* encode result */
+
+ res = ei_x_new_with_version(&xbuf);
+ if (res != 0)
+ goto error;
+
+ res = ei_x_encode_tuple_header(&xbuf, 7);
+ if (res != 0)
+ goto error;
+
+ res = ei_x_encode_atom(&xbuf, "call_result");
+ if (res != 0)
+ goto error;
+
+ res = ei_x_append_buf(&xbuf, lext1, size1);
+ if (res != 0)
+ goto error;
+
+ res = ei_x_encode_long(&xbuf, 4711);
+ if (res != 0)
+ goto error;
+
+ res = ei_x_append_buf(&xbuf, lext2, size2);
+ if (res != 0)
+ goto error;
+
+ res = ei_x_encode_long(&xbuf, 17);
+ if (res != 0)
+ goto error;
+
+ res = ei_x_append_buf(&xbuf, lext3, size3);
+ if (res != 0)
+ goto error;
+
+ res = ei_x_encode_string(&xbuf, "end_of_data");
+ if (res != 0)
+ goto error;
+
+ /* success */
+ ret_size = xbuf.index;
+ *rbuf = driver_alloc(ret_size);
+ memcpy((void *) *rbuf, (void *) xbuf.buff, ret_size);
+
+error:
+
+ if (bin1)
+ driver_free(bin1);
+ if (bin2)
+ driver_free(bin2);
+ if (bin3)
+ driver_free(bin3);
+
+ if (xbuf.buff)
+ ei_x_free(&xbuf);
+
+ return ret_size;
+}
diff --git a/erts/emulator/test/binary_SUITE_data/send_term_local_drv.c b/erts/emulator/test/binary_SUITE_data/send_term_local_drv.c
new file mode 100644
index 0000000000..a5335d4a09
--- /dev/null
+++ b/erts/emulator/test/binary_SUITE_data/send_term_local_drv.c
@@ -0,0 +1,96 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2023. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include "erl_driver.h"
+#include <string.h>
+
+static void stop(ErlDrvData drv_data);
+static ErlDrvData start(ErlDrvPort port,
+ char *command);
+static void output(ErlDrvData drv_data,
+ char *buf, ErlDrvSizeT len);
+
+static ErlDrvEntry send_term_local_drv_entry = {
+ NULL /* init */,
+ start,
+ stop,
+ output,
+ NULL /* ready_input */,
+ NULL /* ready_output */,
+ "send_term_local_drv",
+ NULL /* finish */,
+ NULL /* handle */,
+ NULL /* control */,
+ NULL /* timeout */,
+ NULL /* outputv */,
+ NULL /* ready_async */,
+ NULL /* flush */,
+ NULL /* call */,
+ NULL /* event */,
+ ERL_DRV_EXTENDED_MARKER,
+ ERL_DRV_EXTENDED_MAJOR_VERSION,
+ ERL_DRV_EXTENDED_MINOR_VERSION,
+ ERL_DRV_FLAG_USE_PORT_LOCKING,
+ NULL /* handle2 */,
+ NULL /* handle_monitor */
+};
+
+DRIVER_INIT(send_term_local_drv)
+{
+ return &send_term_local_drv_entry;
+}
+
+static void stop(ErlDrvData drv_data)
+{
+}
+
+static ErlDrvData start(ErlDrvPort port,
+ char *command)
+{
+
+ return (ErlDrvData) port;
+}
+
+static void output(ErlDrvData drv_data,
+ char *buf, ErlDrvSizeT len)
+{
+ ErlDrvPort port = (ErlDrvPort) drv_data;
+ ErlDrvTermData term_port = driver_mk_port(port);
+ ErlDrvTermData caller = driver_caller(port);
+ int res;
+ ErlDrvTermData spec[] = {
+ ERL_DRV_PORT, term_port,
+ ERL_DRV_EXT2TERM, (ErlDrvTermData) buf, len,
+ ERL_DRV_TUPLE, 2
+ };
+ if (0 >= erl_drv_send_term(term_port, caller,
+ spec, sizeof(spec)/sizeof(spec[0]))) {
+ char *bad_term = "bad_term_error";
+ ErlDrvTermData spec[] = {
+ ERL_DRV_PORT, term_port,
+ ERL_DRV_STRING, (ErlDrvTermData) bad_term, strlen(bad_term),
+ ERL_DRV_TUPLE, 2
+ };
+ if (0 >= erl_drv_send_term(term_port, caller, spec,
+ sizeof(spec)/sizeof(spec[0]))) {
+ driver_failure_atom(port, "failed_to_bad_term_error");
+ }
+ }
+}
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 8fd8a847dd..54cbf998c0 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index 34bae47421..923b0ad806 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -974,7 +974,11 @@ external_size(_Term) ->
%% external_size/2
-spec erlang:external_size(Term, Options) -> non_neg_integer() when
Term :: term(),
- Options :: [{minor_version, Version :: non_neg_integer()}].
+ Options :: [compressed |
+ {compressed, Level :: 0..9} |
+ deterministic |
+ {minor_version, Version :: 0..2} |
+ local ].
external_size(_Term, _Options) ->
erlang:nif_error(undefined).
@@ -2768,7 +2772,8 @@ term_to_binary(_Term) ->
Options :: [compressed |
{compressed, Level :: 0..9} |
deterministic |
- {minor_version, Version :: 0..2} ].
+ {minor_version, Version :: 0..2} |
+ local ].
term_to_binary(_Term, _Options) ->
erlang:nif_error(undefined).
@@ -2782,7 +2787,8 @@ term_to_iovec(_Term) ->
Options :: [compressed |
{compressed, Level :: 0..9} |
deterministic |
- {minor_version, Version :: 0..2} ].
+ {minor_version, Version :: 0..2} |
+ local ].
term_to_iovec(_Term, _Options) ->
erlang:nif_error(undefined).
diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
index c39851ba49..de46a22c0a 100644
--- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
+++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c
@@ -19,6 +19,7 @@
*/
#include "ei_runner.h"
+#include <string.h>
/*
* Purpose: Read pids, funs and others without real meaning on the C side
@@ -291,6 +292,7 @@ void decode_encode(struct Type** tv, int nobj)
ei_x_new(&arg);
for (i=0; i<nobj; i++) {
struct Type* t = tv[i];
+ int small_port = 0;
MESSAGE("ei_decode_%s, arg is type %s", t->name, t->type);
@@ -355,9 +357,17 @@ void decode_encode(struct Type** tv, int nobj)
}
}
if (size1 != size2) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
- fail("decode and encode size differs when buf is NULL");
- return;
+ if (strcmp(t->type, "erlang_port") == 0
+ && size1 == size2 + 4
+ && objv[oix].u.port.id <= 0x0fffffff /* 28 bits */) {
+ /* old encoding... */
+ small_port = !0;
+ }
+ else {
+ MESSAGE("size1 = %d, size2 = %d\n",size1,size2);
+ fail("decode and encode size differs when buf is NULL");
+ return;
+ }
}
MESSAGE("ei_encode_%s, arg is type %s", t->name, t->type);
size3 = 0;
@@ -371,9 +381,11 @@ void decode_encode(struct Type** tv, int nobj)
return;
}
if (size1 != size3) {
- MESSAGE("size1 = %d, size2 = %d\n",size1,size3);
- fail("decode and encode size differs");
- return;
+ if (!small_port || size2 != size3) {
+ MESSAGE("size1 = %d, size3 = %d\n",size1,size3);
+ fail("decode and encode size differs");
+ return;
+ }
}
MESSAGE("ei_x_encode_%s, arg is type %s", t->name, t->type);
@@ -394,7 +406,7 @@ void decode_encode(struct Type** tv, int nobj)
}
inp += size1;
- outp += size1;
+ outp += size2;
if (objv[oix].nterms) { /* container term */
if (++oix >= sizeof(objv)/sizeof(*objv))