aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/erl_bif_chksum.c
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/beam/erl_bif_chksum.c')
-rw-r--r--erts/emulator/beam/erl_bif_chksum.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c
new file mode 100644
index 0000000000..445ba00ca7
--- /dev/null
+++ b/erts/emulator/beam/erl_bif_chksum.c
@@ -0,0 +1,612 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "erl_process.h"
+#include "error.h"
+#include "bif.h"
+#include "erl_binary.h"
+#include "big.h"
+#include "zlib.h"
+
+
+typedef void (*ChksumFun)(void *sum_in_out, unsigned char *buf,
+ unsigned buflen);
+
+/* Hidden trap target */
+static BIF_RETTYPE md5_2(BIF_ALIST_2);
+
+static Export chksum_md5_2_exp;
+
+void erts_init_bif_chksum(void)
+{
+ /* Non visual BIF to trap to. */
+ memset(&chksum_md5_2_exp, 0, sizeof(Export));
+ chksum_md5_2_exp.address =
+ &chksum_md5_2_exp.code[3];
+ chksum_md5_2_exp.code[0] = am_erlang;
+ chksum_md5_2_exp.code[1] = am_atom_put("md5_trap",8);
+ chksum_md5_2_exp.code[2] = 2;
+ chksum_md5_2_exp.code[3] =
+ (Eterm) em_apply_bif;
+ chksum_md5_2_exp.code[4] =
+ (Eterm) &md5_2;
+}
+
+
+static Eterm do_chksum(ChksumFun sumfun, Process *p, Eterm ioterm, int left,
+ void *sum, int *res, int *err)
+{
+ Eterm *objp;
+ Eterm obj;
+ int c;
+ DECLARE_ESTACK(stack);
+ unsigned char *bytes = NULL;
+ int numbytes = 0;
+
+ *err = 0;
+ if (left <= 0 || is_nil(ioterm)) {
+ DESTROY_ESTACK(stack);
+ *res = 0;
+ return ioterm;
+ }
+ if(is_binary(ioterm)) {
+ Uint bitoffs;
+ Uint bitsize;
+ Uint size;
+ Eterm res_term = NIL;
+ unsigned char *bytes;
+ byte *temp_alloc = NULL;
+
+ ERTS_GET_BINARY_BYTES(ioterm, bytes, bitoffs, bitsize);
+ if (bitsize != 0) {
+ *res = 0;
+ *err = 1;
+ DESTROY_ESTACK(stack);
+ return NIL;
+ }
+ if (bitoffs != 0) {
+ bytes = erts_get_aligned_binary_bytes(ioterm, &temp_alloc);
+ /* The call to erts_get_aligned_binary_bytes cannot fail as
+ we'we already checked bitsize and that this is a binary */
+ }
+
+ size = binary_size(ioterm);
+
+
+ if (size > left) {
+ Eterm *hp;
+ ErlSubBin *sb;
+ Eterm orig;
+ Uint offset;
+ /* Split the binary in two parts, of which we
+ only process the first */
+ hp = HAlloc(p, ERL_SUB_BIN_SIZE);
+ sb = (ErlSubBin *) hp;
+ ERTS_GET_REAL_BIN(ioterm, orig, offset, bitoffs, bitsize);
+ sb->thing_word = HEADER_SUB_BIN;
+ sb->size = size - left;
+ sb->offs = offset + left;
+ sb->orig = orig;
+ sb->bitoffs = bitoffs;
+ sb->bitsize = bitsize;
+ sb->is_writable = 0;
+ res_term = make_binary(sb);
+ size = left;
+ }
+ (*sumfun)(sum, bytes, size);
+ *res = size;
+ DESTROY_ESTACK(stack);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ return res_term;
+ }
+
+ if (!is_list(ioterm)) {
+ *res = 0;
+ *err = 1;
+ DESTROY_ESTACK(stack);
+ return NIL;
+ }
+
+ /* OK a list, needs to be processed in order, handling each flat list-level
+ as they occur, just like io_list_to_binary would */
+ *res = 0;
+ ESTACK_PUSH(stack,ioterm);
+ while (!ESTACK_ISEMPTY(stack) && left) {
+ ioterm = ESTACK_POP(stack);
+ if (is_nil(ioterm)) {
+ /* ignore empty lists */
+ continue;
+ }
+ if(is_list(ioterm)) {
+L_Again: /* Restart with sublist, old listend was pushed on stack */
+ objp = list_val(ioterm);
+ obj = CAR(objp);
+ for(;;) { /* loop over one flat list of bytes and binaries
+ until sublist or list end is encountered */
+ if (is_byte(obj)) {
+ int bsize = 0;
+ for(;;) {
+ if (bsize >= numbytes) {
+ if (!bytes) {
+ bytes = erts_alloc(ERTS_ALC_T_TMP,
+ numbytes = 500);
+ } else {
+ if (numbytes > left) {
+ numbytes += left;
+ } else {
+ numbytes *= 2;
+ }
+ bytes = erts_realloc(ERTS_ALC_T_TMP, bytes,
+ numbytes);
+ }
+ }
+ bytes[bsize++] = (unsigned char) unsigned_val(obj);
+ --left;
+ ioterm = CDR(objp);
+ if (!is_list(ioterm)) {
+ break;
+ }
+ objp = list_val(ioterm);
+ obj = CAR(objp);
+ if (!is_byte(obj))
+ break;
+ if (!left) {
+ break;
+ }
+ }
+ (*sumfun)(sum, bytes, bsize);
+ *res += bsize;
+ } else if (is_nil(obj)) {
+ ioterm = CDR(objp);
+ if (!is_list(ioterm)) {
+ break;
+ }
+ objp = list_val(ioterm);
+ obj = CAR(objp);
+ } else if (is_list(obj)) {
+ /* push rest of list for later processing, start
+ again with sublist */
+ ESTACK_PUSH(stack,CDR(objp));
+ ioterm = obj;
+ goto L_Again;
+ } else if (is_binary(obj)) {
+ int sres, serr;
+ Eterm rest_term;
+ rest_term = do_chksum(sumfun, p, obj, left, sum, &sres,
+ &serr);
+ *res += sres;
+ if (serr != 0) {
+ *err = 1;
+ DESTROY_ESTACK(stack);
+ if (bytes != NULL)
+ erts_free(ERTS_ALC_T_TMP, bytes);
+ return NIL;
+ }
+ left -= sres;
+ if (rest_term != NIL) {
+ Eterm *hp;
+ hp = HAlloc(p, 2);
+ obj = CDR(objp);
+ ioterm = CONS(hp, rest_term, obj);
+ left = 0;
+ break;
+ }
+ ioterm = CDR(objp);
+ if (is_list(ioterm)) {
+ /* objp and obj need to be updated if
+ loop is to continue */
+ objp = list_val(ioterm);
+ obj = CAR(objp);
+ }
+ } else {
+ *err = 1;
+ DESTROY_ESTACK(stack);
+ if (bytes != NULL)
+ erts_free(ERTS_ALC_T_TMP, bytes);
+ return NIL;
+ }
+ if (!left || is_nil(ioterm) || !is_list(ioterm)) {
+ break;
+ }
+ } /* for(;;) */
+ } /* is_list(ioterm) */
+
+ if (!left) {
+#ifdef ALLOW_BYTE_TAIL
+ if (is_byte(ioterm)) {
+ /* inproper list with byte tail*/
+ Eterm *hp;
+ hp = HAlloc(p, 2);
+ ioterm = CONS(hp, ioterm, NIL);
+ }
+#else
+ ;
+#endif
+ } else if (!is_list(ioterm) && !is_nil(ioterm)) {
+ /* inproper list end */
+#ifdef ALLOW_BYTE_TAIL
+ if (is_byte(ioterm)) {
+ unsigned char b[1];
+ b[0] = (unsigned char) unsigned_val(ioterm);
+ (*sumfun)(sum, b, 1);
+ ++(*res);
+ --left;
+ ioterm = NIL;
+ } else
+#endif
+ if is_binary(ioterm) {
+ int sres, serr;
+ ioterm = do_chksum(sumfun, p, ioterm, left, sum, &sres, &serr);
+ *res +=sres;
+ if (serr != 0) {
+ *err = 1;
+ DESTROY_ESTACK(stack);
+ if (bytes != NULL)
+ erts_free(ERTS_ALC_T_TMP, bytes);
+ return NIL;
+ }
+ left -= sres;
+ } else {
+ *err = 1;
+ DESTROY_ESTACK(stack);
+ if (bytes != NULL)
+ erts_free(ERTS_ALC_T_TMP, bytes);
+ return NIL;
+ }
+ }
+ } /* while left and not estack empty */
+ c = ESTACK_COUNT(stack);
+ if (c > 0) {
+ Eterm *hp = HAlloc(p,2*c);
+ while(!ESTACK_ISEMPTY(stack)) {
+ Eterm st = ESTACK_POP(stack);
+ ioterm = CONS(hp, ioterm, st);
+ hp += 2;
+ }
+ }
+ DESTROY_ESTACK(stack);
+ if (bytes != NULL)
+ erts_free(ERTS_ALC_T_TMP, bytes);
+ return ioterm;
+}
+
+static void adler32_wrap(void *vsum, unsigned char *buf, unsigned buflen)
+{
+ unsigned long sum = *((unsigned long *) vsum);
+ sum = adler32(sum,buf,buflen);
+ *((unsigned long *) vsum) = sum;
+}
+
+static void crc32_wrap(void *vsum, unsigned char *buf, unsigned buflen)
+{
+ unsigned long sum = *((unsigned long *) vsum);
+ sum = crc32(sum,buf,buflen);
+ *((unsigned long *) vsum) = sum;
+}
+
+static void md5_wrap(void *vsum, unsigned char *buf, unsigned buflen)
+{
+ MD5_CTX *ctx = ((MD5_CTX *) vsum);
+ MD5Update(ctx,buf,buflen);
+}
+
+#define BYTES_PER_REDUCTION 10
+#define CHUNK_PER_SCHEDULE (BYTES_PER_REDUCTION * CONTEXT_REDS)
+
+BIF_RETTYPE
+crc32_1(BIF_ALIST_1)
+{
+ unsigned long chksum;
+ int res, err;
+ Eterm rest,res_sum;
+ chksum = crc32(0,NULL,0);
+
+ rest = do_chksum(&crc32_wrap,BIF_P,BIF_ARG_1,CHUNK_PER_SCHEDULE,
+ (void *) &chksum,&res,
+ &err);
+ BUMP_REDS(BIF_P,res / BYTES_PER_REDUCTION);
+ if (err != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ res_sum = erts_make_integer(chksum,BIF_P);
+ if (rest != NIL) {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest);
+ }
+ BIF_RET(res_sum);
+}
+
+BIF_RETTYPE
+crc32_2(BIF_ALIST_2)
+{
+ unsigned long chksum;
+ int res, err;
+ Eterm rest,res_sum;
+ Uint u;
+ if (!term_to_Uint(BIF_ARG_1, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ chksum = (unsigned long) u;
+
+ rest = do_chksum(&crc32_wrap,BIF_P,BIF_ARG_2,CHUNK_PER_SCHEDULE,
+ (void *) &chksum,&res,
+ &err);
+ BUMP_REDS(BIF_P,res / BYTES_PER_REDUCTION);
+ if (err != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ res_sum = erts_make_integer(chksum,BIF_P);
+ if (rest != NIL) {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP2(bif_export[BIF_crc32_2], BIF_P, res_sum, rest);
+ }
+ BIF_RET(res_sum);
+}
+
+BIF_RETTYPE
+crc32_combine_3(BIF_ALIST_3)
+{
+ unsigned long chksum1,chksum2;
+ z_off_t length;
+ Uint32 res;
+ Eterm res_sum;
+ Uint u;
+
+ if (!term_to_Uint(BIF_ARG_1, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ chksum1 = (unsigned long) u;
+
+ if (!term_to_Uint(BIF_ARG_2, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ chksum2 = (unsigned long) u;
+
+ if (!term_to_Uint(BIF_ARG_3, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ length = (z_off_t) u;
+
+ res = (Uint32) crc32_combine(chksum1,chksum2,length);
+
+ res_sum = erts_make_integer(res,BIF_P);
+ BIF_RET(res_sum);
+}
+
+BIF_RETTYPE
+adler32_1(BIF_ALIST_1)
+{
+ unsigned long chksum;
+ int res, err;
+ Eterm rest,res_sum;
+ chksum = adler32(0,NULL,0);
+
+ rest = do_chksum(&adler32_wrap,BIF_P,BIF_ARG_1,CHUNK_PER_SCHEDULE,
+ (void *) &chksum,&res,
+ &err);
+ BUMP_REDS(BIF_P,res / BYTES_PER_REDUCTION);
+ if (err != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ res_sum = erts_make_integer(chksum,BIF_P);
+ if (rest != NIL) {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest);
+ }
+ BIF_RET(res_sum);
+}
+
+BIF_RETTYPE
+adler32_2(BIF_ALIST_2)
+{
+ unsigned long chksum;
+ int res, err;
+ Eterm rest,res_sum;
+ Uint u;
+ if (!term_to_Uint(BIF_ARG_1, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ chksum = (unsigned long) u;
+
+ rest = do_chksum(&adler32_wrap,BIF_P,BIF_ARG_2,CHUNK_PER_SCHEDULE,
+ (void *) &chksum,&res,
+ &err);
+ BUMP_REDS(BIF_P,res / BYTES_PER_REDUCTION);
+ if (err != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ res_sum = erts_make_integer(chksum,BIF_P);
+ if (rest != NIL) {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP2(bif_export[BIF_adler32_2], BIF_P, res_sum, rest);
+ }
+ BIF_RET(res_sum);
+}
+
+BIF_RETTYPE
+adler32_combine_3(BIF_ALIST_3)
+{
+ unsigned long chksum1,chksum2;
+ z_off_t length;
+ Uint32 res;
+ Eterm res_sum;
+ Uint u;
+
+ if (!term_to_Uint(BIF_ARG_1, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ chksum1 = (unsigned long) u;
+
+ if (!term_to_Uint(BIF_ARG_2, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ chksum2 = (unsigned long) u;
+
+ if (!term_to_Uint(BIF_ARG_3, &u) || ((u >> 16) >> 16) != 0) {
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ length = (z_off_t) u;
+
+ if (length == 0) { /* Workaround for unexpected behaviour in zlib. */
+ res = (Uint32) chksum1;
+ } else {
+ res = (Uint32) adler32_combine(chksum1,chksum2,length);
+ }
+
+ res_sum = erts_make_integer(res,BIF_P);
+ BIF_RET(res_sum);
+}
+
+
+BIF_RETTYPE
+md5_1(BIF_ALIST_1)
+{
+ Eterm bin;
+ byte* bytes;
+ Eterm rest;
+ int res, err;
+
+ MD5_CTX context;
+ MD5Init(&context);
+
+ rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_1,100,(void *) &context,&res,
+ &err);
+ if (err != 0) {
+ BUMP_REDS(BIF_P,res);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (rest != NIL) {
+ BUMP_ALL_REDS(BIF_P);
+ bin = new_binary(BIF_P, (byte *) &context, sizeof(MD5_CTX));
+ BIF_TRAP2(&chksum_md5_2_exp, BIF_P, bin, rest);
+ }
+ BUMP_REDS(BIF_P,res);
+ bin = new_binary(BIF_P, (byte *)NULL, 16);
+ bytes = binary_bytes(bin);
+ MD5Final(bytes, &context);
+ BIF_RET(bin);
+}
+
+/* Hidden trap target */
+static BIF_RETTYPE
+md5_2(BIF_ALIST_2)
+{
+ byte *bytes;
+ MD5_CTX context;
+ Eterm rest;
+ Eterm bin;
+ int res, err;
+
+ /* No need to check context, this function cannot be called with unaligned
+ or badly sized context as it's always trapped to. */
+ bytes = binary_bytes(BIF_ARG_1);
+ memcpy(&context,bytes,sizeof(MD5_CTX));
+ rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res,
+ &err);
+ if (err != 0) {
+ BUMP_REDS(BIF_P,res);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (rest != NIL) {
+ BUMP_ALL_REDS(BIF_P);
+ bin = new_binary(BIF_P, (byte *) &context, sizeof(MD5_CTX));
+ BIF_TRAP2(&chksum_md5_2_exp, BIF_P, bin, rest);
+ }
+ BUMP_REDS(BIF_P,res);
+ bin = new_binary(BIF_P, (byte *)NULL, 16);
+ bytes = binary_bytes(bin);
+ MD5Final(bytes, &context);
+ BIF_RET(bin);
+}
+
+BIF_RETTYPE
+md5_init_0(BIF_ALIST_0)
+{
+ Eterm bin;
+ byte* bytes;
+
+ bin = erts_new_heap_binary(BIF_P, (byte *)NULL, sizeof(MD5_CTX), &bytes);
+ MD5Init((MD5_CTX *)bytes);
+ BIF_RET(bin);
+}
+
+BIF_RETTYPE
+md5_update_2(BIF_ALIST_2)
+{
+ byte *bytes;
+ MD5_CTX context;
+ Eterm rest;
+ Eterm bin;
+ int res, err;
+ byte *temp_alloc = NULL;
+
+ if ((bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc)) == NULL) {
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (binary_size(BIF_ARG_1) != sizeof(MD5_CTX)) {
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ memcpy(&context,bytes,sizeof(MD5_CTX));
+ erts_free_aligned_binary_bytes(temp_alloc);
+ rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res,
+ &err);
+ if (err != 0) {
+ BUMP_REDS(BIF_P,res);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ bin = new_binary(BIF_P, (byte *) &context, sizeof(MD5_CTX));
+ if (rest != NIL) {
+ BUMP_ALL_REDS(BIF_P);
+ BIF_TRAP2(bif_export[BIF_md5_update_2], BIF_P, bin, rest);
+ }
+ BUMP_REDS(BIF_P,res);
+ BIF_RET(bin);
+}
+
+BIF_RETTYPE
+md5_final_1(BIF_ALIST_1)
+{
+ Eterm bin;
+ byte* context;
+ byte* result;
+ MD5_CTX ctx_copy;
+ byte* temp_alloc = NULL;
+
+ if ((context = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc)) == NULL) {
+ error:
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_ERROR(BIF_P, BADARG);
+ }
+ if (binary_size(BIF_ARG_1) != sizeof(MD5_CTX)) {
+ goto error;
+ }
+ bin = erts_new_heap_binary(BIF_P, (byte *)NULL, 16, &result);
+ memcpy(&ctx_copy, context, sizeof(MD5_CTX));
+ erts_free_aligned_binary_bytes(temp_alloc);
+ MD5Final(result, &ctx_copy);
+ BIF_RET(bin);
+}