|
60 | 60 | #include "term_typedef.h"
|
61 | 61 | #include "unicode.h"
|
62 | 62 | #include "utils.h"
|
| 63 | +#ifdef WITH_ZLIB |
| 64 | +#include "zlib.h" |
| 65 | +#endif |
63 | 66 |
|
64 | 67 | #define MAX_NIF_NAME_LEN 260
|
65 | 68 | #define FLOAT_BUF_SIZE 64
|
@@ -198,6 +201,7 @@ static term nif_maps_next(Context *ctx, int argc, term argv[]);
|
198 | 201 | static term nif_unicode_characters_to_list(Context *ctx, int argc, term argv[]);
|
199 | 202 | static term nif_unicode_characters_to_binary(Context *ctx, int argc, term argv[]);
|
200 | 203 | static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[]);
|
| 204 | +static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]); |
201 | 205 |
|
202 | 206 | #define DECLARE_MATH_NIF_FUN(moniker) \
|
203 | 207 | static term nif_math_##moniker(Context *ctx, int argc, term argv[]);
|
@@ -858,6 +862,12 @@ static const struct Nif erlang_lists_subtract_nif =
|
858 | 862 | .base.type = NIFFunctionType,
|
859 | 863 | .nif_ptr = nif_erlang_lists_subtract
|
860 | 864 | };
|
| 865 | +static const struct Nif zlib_compress_nif = |
| 866 | +{ |
| 867 | + .base.type = NIFFunctionType, |
| 868 | + .nif_ptr = nif_zlib_compress_1 |
| 869 | +}; |
| 870 | + |
861 | 871 |
|
862 | 872 | #define DEFINE_MATH_NIF(moniker) \
|
863 | 873 | static const struct Nif math_##moniker##_nif = \
|
@@ -2520,58 +2530,77 @@ static term nif_erlang_float_to_list(Context *ctx, int argc, term argv[])
|
2520 | 2530 | return make_list_from_ascii_buf((uint8_t *) float_buf, len, ctx);
|
2521 | 2531 | }
|
2522 | 2532 |
|
2523 |
| -static term nif_erlang_list_to_binary_1(Context *ctx, int argc, term argv[]) |
| 2533 | +static term iolist_to_buffer(term list, char **buf, size_t *size) |
2524 | 2534 | {
|
2525 |
| - UNUSED(argc); |
2526 |
| - |
2527 |
| - term t = argv[0]; |
2528 |
| - VALIDATE_VALUE(t, term_is_list); |
| 2535 | + *buf = NULL; |
| 2536 | + *size = 0; |
2529 | 2537 |
|
2530 | 2538 | size_t bin_size;
|
2531 |
| - switch (interop_iolist_size(t, &bin_size)) { |
| 2539 | + switch (interop_iolist_size(list, &bin_size)) { |
2532 | 2540 | case InteropOk:
|
2533 | 2541 | break;
|
2534 | 2542 | case InteropMemoryAllocFail:
|
2535 |
| - RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
| 2543 | + return OUT_OF_MEMORY_ATOM; |
2536 | 2544 | case InteropBadArg:
|
2537 |
| - RAISE_ERROR(BADARG_ATOM); |
| 2545 | + return BADARG_ATOM; |
| 2546 | + } |
| 2547 | + |
| 2548 | + if (bin_size == 0) { |
| 2549 | + return OK_ATOM; |
2538 | 2550 | }
|
2539 | 2551 |
|
2540 | 2552 | char *bin_buf = NULL;
|
2541 |
| - bool buf_allocated = true; |
2542 |
| - if (bin_size > 0) { |
2543 |
| - bin_buf = malloc(bin_size); |
2544 |
| - if (IS_NULL_PTR(bin_buf)) { |
2545 |
| - RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
2546 |
| - } |
| 2553 | + bin_buf = malloc(bin_size * sizeof(char)); |
| 2554 | + if (IS_NULL_PTR(bin_buf)) { |
| 2555 | + return OUT_OF_MEMORY_ATOM; |
| 2556 | + } |
2547 | 2557 |
|
2548 |
| - switch (interop_write_iolist(t, bin_buf)) { |
2549 |
| - case InteropOk: |
2550 |
| - break; |
2551 |
| - case InteropMemoryAllocFail: |
2552 |
| - free(bin_buf); |
2553 |
| - RAISE_ERROR(OUT_OF_MEMORY_ATOM); |
2554 |
| - case InteropBadArg: |
2555 |
| - free(bin_buf); |
2556 |
| - RAISE_ERROR(BADARG_ATOM); |
2557 |
| - } |
| 2558 | + switch (interop_write_iolist(list, bin_buf)) { |
| 2559 | + case InteropOk: |
| 2560 | + break; |
| 2561 | + case InteropMemoryAllocFail: |
| 2562 | + free(bin_buf); |
| 2563 | + return OUT_OF_MEMORY_ATOM; |
| 2564 | + case InteropBadArg: |
| 2565 | + free(bin_buf); |
| 2566 | + return BADARG_ATOM; |
| 2567 | + } |
| 2568 | + |
| 2569 | + *buf = bin_buf; |
| 2570 | + *size = bin_size; |
| 2571 | + return OK_ATOM; |
| 2572 | +} |
| 2573 | + |
| 2574 | +static term nif_erlang_list_to_binary_1(Context *ctx, int argc, term argv[]) |
| 2575 | +{ |
| 2576 | + UNUSED(argc); |
| 2577 | + |
| 2578 | + term t = argv[0]; |
| 2579 | + VALIDATE_VALUE(t, term_is_list); |
| 2580 | + |
| 2581 | + char *bin_buf = NULL; |
| 2582 | + char *alloc_ptr = NULL; |
| 2583 | + size_t bin_size = 0; |
| 2584 | + |
| 2585 | + term status = iolist_to_buffer(t, &bin_buf, &bin_size); |
| 2586 | + if (UNLIKELY(status != OK_ATOM)) { |
| 2587 | + RAISE_ERROR(status); |
| 2588 | + } |
| 2589 | + bool allocated = bin_size > 0; |
| 2590 | + if (allocated) { |
| 2591 | + alloc_ptr = bin_buf; |
2558 | 2592 | } else {
|
2559 | 2593 | bin_buf = "";
|
2560 |
| - buf_allocated = false; |
| 2594 | + bin_size = 0; |
2561 | 2595 | }
|
2562 | 2596 |
|
2563 |
| - if (UNLIKELY(memory_ensure_free_with_roots(ctx, term_binary_heap_size(bin_size), 1, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) { |
2564 |
| - if (buf_allocated) { |
2565 |
| - free(bin_buf); |
2566 |
| - } |
| 2597 | + if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(bin_size)) != MEMORY_GC_OK)) { |
| 2598 | + free(alloc_ptr); |
2567 | 2599 | RAISE_ERROR(OUT_OF_MEMORY_ATOM);
|
2568 | 2600 | }
|
2569 | 2601 | term bin_res = term_from_literal_binary(bin_buf, bin_size, &ctx->heap, ctx->global);
|
2570 | 2602 |
|
2571 |
| - if (buf_allocated) { |
2572 |
| - free(bin_buf); |
2573 |
| - } |
2574 |
| - |
| 2603 | + free(alloc_ptr); |
2575 | 2604 | return bin_res;
|
2576 | 2605 | }
|
2577 | 2606 |
|
@@ -5837,6 +5866,85 @@ static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[])
|
5837 | 5866 | free(cons);
|
5838 | 5867 | return result;
|
5839 | 5868 | }
|
| 5869 | + |
| 5870 | +#ifdef WITH_ZLIB |
| 5871 | +static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]) |
| 5872 | +{ |
| 5873 | + UNUSED(argc) |
| 5874 | + |
| 5875 | + term error = OK_ATOM; |
| 5876 | + |
| 5877 | + Bytef *output_buf = NULL; |
| 5878 | + char *input_buf = NULL; |
| 5879 | + size_t input_size = 0; |
| 5880 | + char *alloc_ptr = NULL; |
| 5881 | + |
| 5882 | + term input = argv[0]; |
| 5883 | + if (LIKELY(term_is_list(input))) { |
| 5884 | + term status = iolist_to_buffer(input, &input_buf, &input_size); |
| 5885 | + if (UNLIKELY(status != OK_ATOM)) { |
| 5886 | + error = status; |
| 5887 | + goto cleanup; |
| 5888 | + } |
| 5889 | + |
| 5890 | + bool allocated = input_size > 0; |
| 5891 | + if (allocated) { |
| 5892 | + alloc_ptr = input_buf; |
| 5893 | + } else { |
| 5894 | + input_buf = ""; |
| 5895 | + input_size = 0; |
| 5896 | + } |
| 5897 | + } else if (LIKELY(term_is_binary(input))) { |
| 5898 | + input_buf = (char *) term_binary_data(input); |
| 5899 | + input_size = term_binary_size(input); |
| 5900 | + } else { |
| 5901 | + error = BADARG_ATOM; |
| 5902 | + goto cleanup; |
| 5903 | + } |
| 5904 | + |
| 5905 | + // to_allocate is an upper bound for compression size |
| 5906 | + // changes to actual size after calling compress |
| 5907 | + uLong to_allocate = compressBound(input_size); |
| 5908 | + // TODO: use `to_allocate` binary directly instead of allocating a buffer |
| 5909 | + // Currently not possible, there's no way to shrink backing buffer for the binary |
| 5910 | + output_buf = malloc(to_allocate); |
| 5911 | + if (IS_NULL_PTR(output_buf)) { |
| 5912 | + error = OUT_OF_MEMORY_ATOM; |
| 5913 | + goto cleanup; |
| 5914 | + } |
| 5915 | + |
| 5916 | + int z_ret = compress(output_buf, &to_allocate, (const Bytef *) input_buf, input_size); |
| 5917 | + if (UNLIKELY(z_ret != Z_OK)) { |
| 5918 | + error = OUT_OF_MEMORY_ATOM; |
| 5919 | + goto cleanup; |
| 5920 | + } |
| 5921 | + |
| 5922 | + if (UNLIKELY(memory_ensure_free(ctx, term_binary_data_size_in_terms(to_allocate)) != MEMORY_GC_OK)) { |
| 5923 | + error = OUT_OF_MEMORY_ATOM; |
| 5924 | + goto cleanup; |
| 5925 | + } |
| 5926 | + term bin_res = term_from_literal_binary(output_buf, to_allocate, &ctx->heap, ctx->global); |
| 5927 | + |
| 5928 | +cleanup: |
| 5929 | + free(alloc_ptr); |
| 5930 | + free(output_buf); |
| 5931 | + if (UNLIKELY(error != OK_ATOM)) { |
| 5932 | + RAISE_ERROR(error); |
| 5933 | + } |
| 5934 | + return bin_res; |
| 5935 | +} |
| 5936 | +#endif |
| 5937 | +#ifndef WITH_ZLIB |
| 5938 | +static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]) |
| 5939 | +{ |
| 5940 | + UNUSED(argc) |
| 5941 | + UNUSED(argv) |
| 5942 | + UNUSED(ctx) |
| 5943 | + fprintf(stderr, "Error: zlib library needed to use zlib:compress/1\n"); |
| 5944 | + RAISE_ERROR(UNDEFINED_ATOM); |
| 5945 | +} |
| 5946 | +#endif |
| 5947 | + |
5840 | 5948 | //
|
5841 | 5949 | // MAINTENANCE NOTE: Exception handling for fp operations using math
|
5842 | 5950 | // error handling is designed to be thread-safe, as errors are specified
|
|
0 commit comments