Skip to content

Commit 55d0eaf

Browse files
TheSobkiewiczjakub-gonet
authored andcommitted
1 parent bafd18b commit 55d0eaf

File tree

6 files changed

+159
-16
lines changed

6 files changed

+159
-16
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2222
- Added support for `ets:update_counter/3` and `ets:update_counter/4`.
2323
- Added `erlang:+/1`
2424
- Added `lists:append/1` and `lists:append/2`
25+
- Added `zlib:compress/1`
2526

2627
### Fixed
2728
- ESP32: improved sntp sync speed from a cold boot.

src/libAtomVM/nifs.c

+91-16
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@
5757
#include "term.h"
5858
#include "unicode.h"
5959
#include "utils.h"
60+
#ifdef WITH_ZLIB
61+
#include "zlib.h"
62+
#endif
6063

6164
#define MAX_NIF_NAME_LEN 260
6265
#define FLOAT_BUF_SIZE 64
@@ -186,6 +189,7 @@ static term nif_maps_next(Context *ctx, int argc, term argv[]);
186189
static term nif_unicode_characters_to_list(Context *ctx, int argc, term argv[]);
187190
static term nif_unicode_characters_to_binary(Context *ctx, int argc, term argv[]);
188191
static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[]);
192+
static term nif_zlib_compress_1(Context *ctx, int argc, term argv[]);
189193

190194
#define DECLARE_MATH_NIF_FUN(moniker) \
191195
static term nif_math_##moniker(Context *ctx, int argc, term argv[]);
@@ -792,6 +796,12 @@ static const struct Nif erlang_lists_subtract_nif =
792796
.base.type = NIFFunctionType,
793797
.nif_ptr = nif_erlang_lists_subtract
794798
};
799+
static const struct Nif zlib_compress_nif =
800+
{
801+
.base.type = NIFFunctionType,
802+
.nif_ptr = nif_zlib_compress_1
803+
};
804+
795805

796806
#define DEFINE_MATH_NIF(moniker) \
797807
static const struct Nif math_##moniker##_nif = \
@@ -2411,59 +2421,69 @@ static term nif_erlang_float_to_list(Context *ctx, int argc, term argv[])
24112421
return make_list_from_ascii_buf((uint8_t *) float_buf, len, ctx);
24122422
}
24132423

2414-
static term nif_erlang_list_to_binary_1(Context *ctx, int argc, term argv[])
2424+
static term list_to_binary(term list, term *ret, Context *ctx)
24152425
{
2416-
UNUSED(argc);
2417-
2418-
term t = argv[0];
2419-
VALIDATE_VALUE(t, term_is_list);
2420-
24212426
size_t bin_size;
2422-
switch (interop_iolist_size(t, &bin_size)) {
2427+
switch (interop_iolist_size(list, &bin_size)) {
24232428
case InteropOk:
24242429
break;
24252430
case InteropMemoryAllocFail:
2426-
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
2431+
return OUT_OF_MEMORY_ATOM;
24272432
case InteropBadArg:
2428-
RAISE_ERROR(BADARG_ATOM);
2433+
return BADARG_ATOM;
24292434
}
24302435

24312436
char *bin_buf = NULL;
24322437
bool buf_allocated = true;
24332438
if (bin_size > 0) {
24342439
bin_buf = malloc(bin_size);
24352440
if (IS_NULL_PTR(bin_buf)) {
2436-
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
2441+
return OUT_OF_MEMORY_ATOM;
24372442
}
24382443

2439-
switch (interop_write_iolist(t, bin_buf)) {
2444+
switch (interop_write_iolist(list, bin_buf)) {
24402445
case InteropOk:
24412446
break;
24422447
case InteropMemoryAllocFail:
24432448
free(bin_buf);
2444-
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
2449+
return OUT_OF_MEMORY_ATOM;
24452450
case InteropBadArg:
24462451
free(bin_buf);
2447-
RAISE_ERROR(BADARG_ATOM);
2452+
return BADARG_ATOM;
24482453
}
24492454
} else {
24502455
bin_buf = "";
24512456
buf_allocated = false;
24522457
}
24532458

2454-
if (UNLIKELY(memory_ensure_free_with_roots(ctx, term_binary_heap_size(bin_size), 1, argv, MEMORY_CAN_SHRINK) != MEMORY_GC_OK)) {
2459+
if (UNLIKELY(memory_ensure_free(ctx, term_binary_heap_size(bin_size)) != MEMORY_GC_OK)) {
24552460
if (buf_allocated) {
24562461
free(bin_buf);
24572462
}
2458-
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
2463+
return OUT_OF_MEMORY_ATOM;
24592464
}
24602465
term bin_res = term_from_literal_binary(bin_buf, bin_size, &ctx->heap, ctx->global);
24612466

24622467
if (buf_allocated) {
24632468
free(bin_buf);
24642469
}
2470+
*ret = bin_res;
24652471

2466-
return bin_res;
2472+
return OK_ATOM;
2473+
}
2474+
2475+
static term nif_erlang_list_to_binary_1(Context *ctx, int argc, term argv[])
2476+
{
2477+
UNUSED(argc);
2478+
2479+
term t = argv[0];
2480+
VALIDATE_VALUE(t, term_is_list);
2481+
term ret;
2482+
term result = list_to_binary(t, &ret, ctx);
2483+
if (UNLIKELY(result != OK_ATOM)) {
2484+
RAISE_ERROR(result);
2485+
}
2486+
return ret;
24672487
}
24682488

24692489
static avm_int_t to_digit_index(avm_int_t character)
@@ -5257,6 +5277,61 @@ static term nif_erlang_lists_subtract(Context *ctx, int argc, term argv[])
52575277
free(cons);
52585278
return result;
52595279
}
5280+
5281+
#ifdef WITH_ZLIB
5282+
static term nif_zlib_compress_1(Context *ctx, int argc, term argv[])
5283+
{
5284+
UNUSED(argc)
5285+
term to_compress = argv[0];
5286+
if (term_is_list(to_compress)) {
5287+
term to_compress_binary;
5288+
term result = list_to_binary(to_compress, &to_compress_binary, ctx);
5289+
if (result != OK_ATOM) {
5290+
RAISE_ERROR(result);
5291+
}
5292+
to_compress = to_compress_binary;
5293+
}
5294+
if (UNLIKELY(!term_is_binary(to_compress))) {
5295+
RAISE_ERROR(BADARG_ATOM);
5296+
}
5297+
5298+
size_t size = term_binary_size(to_compress);
5299+
// to_allocate is an upper bound for compression size
5300+
// changes to actual size after calling compress
5301+
uLong to_allocate = compressBound(size);
5302+
// Bytef is an unsigned char, term_binary_data return const *char
5303+
const Bytef *to_compress_data = (const Bytef *) term_binary_data(to_compress);
5304+
Bytef *compressed = malloc(to_allocate);
5305+
if (IS_NULL_PTR(compressed)) {
5306+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
5307+
}
5308+
5309+
int z_ret = compress(compressed, &to_allocate, to_compress_data, size);
5310+
if (UNLIKELY(z_ret != Z_OK)) {
5311+
free(compressed);
5312+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
5313+
}
5314+
5315+
if (UNLIKELY(memory_ensure_free(ctx, term_binary_data_size_in_terms(to_allocate)) != MEMORY_GC_OK)) {
5316+
free(compressed);
5317+
RAISE_ERROR(OUT_OF_MEMORY_ATOM);
5318+
}
5319+
term bin_res = term_from_literal_binary(compressed, to_allocate, &ctx->heap, ctx->global);
5320+
free(compressed);
5321+
return bin_res;
5322+
}
5323+
#endif
5324+
#ifndef WITH_ZLIB
5325+
static term nif_zlib_compress_1(Context *ctx, int argc, term argv[])
5326+
{
5327+
UNUSED(argc)
5328+
UNUSED(argv)
5329+
UNUSED(ctx)
5330+
fprintf(stderr, "Error: zlib library needed to use zlib:compress/1\n");
5331+
RAISE_ERROR(UNDEFINED_ATOM);
5332+
}
5333+
#endif
5334+
52605335
//
52615336
// MAINTENANCE NOTE: Exception handling for fp operations using math
52625337
// error handling is designed to be thread-safe, as errors are specified

src/libAtomVM/nifs.gperf

+1
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,4 @@ math:sinh/1, &math_sinh_nif
192192
math:sqrt/1, &math_sqrt_nif
193193
math:tan/1, &math_tan_nif
194194
math:tanh/1, &math_tanh_nif
195+
zlib:compress/1, &zlib_compress_nif

tests/erlang_tests/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ compile_erlang(test_unicode)
281281
compile_erlang(test_binary_part)
282282
compile_erlang(test_binary_split)
283283

284+
compile_erlang(test_zlib_compress)
285+
284286
compile_erlang(plusone)
285287
compile_erlang(plusone2)
286288
compile_erlang(minusone)
@@ -762,6 +764,8 @@ add_custom_target(erlang_test_modules DEPENDS
762764
test_binary_part.beam
763765
test_binary_split.beam
764766

767+
test_zlib_compress.beam
768+
765769
plusone.beam
766770
plusone2.beam
767771
minusone.beam
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
%
2+
% This file is part of AtomVM.
3+
%
4+
% Copyright 2024 Tomasz Sobkiewicz <[email protected]>
5+
%
6+
% Licensed under the Apache License, Version 2.0 (the "License");
7+
% you may not use this file except in compliance with the License.
8+
% You may obtain a copy of the License at
9+
%
10+
% http://www.apache.org/licenses/LICENSE-2.0
11+
%
12+
% Unless required by applicable law or agreed to in writing, software
13+
% distributed under the License is distributed on an "AS IS" BASIS,
14+
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
% See the License for the specific language governing permissions and
16+
% limitations under the License.
17+
%
18+
% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
19+
%
20+
21+
-module(test_zlib_compress).
22+
23+
-export([start/0]).
24+
25+
start() ->
26+
ok = test_zlib_compress_binary(),
27+
ok = test_zlib_compress_iolist(),
28+
0.
29+
30+
test_zlib_compress_binary() ->
31+
ToCompress =
32+
<<"Actually, this sentence could be a bit shorter. We definitelly need to compress it.">>,
33+
% Data from original erlang implementation
34+
ProperlyCompressed =
35+
<<120, 156, 13, 203, 203, 13, 128, 32, 16, 4, 208, 86, 166, 0, 67, 15, 86, 225, 153, 207,
36+
24, 54, 65, 48, 236, 112, 176, 123, 185, 191, 119, 102, 173, 216, 218, 119, 64, 213, 28,
37+
206, 46, 246, 76, 228, 177, 90, 65, 34, 34, 146, 9, 94, 199, 20, 103, 192, 69, 20, 222,
38+
214, 77, 220, 11, 157, 44, 208, 216, 252, 121, 39, 221, 97, 10, 63, 241, 84, 30, 23>>,
39+
ProperlyCompressed = zlib:compress(ToCompress),
40+
ok.
41+
test_zlib_compress_iolist() ->
42+
ToCompressList = [
43+
<<"Actually,">>,
44+
" this sentence ",
45+
<<"could be ">>,
46+
[[<<"a ">>, [<<"b">>, <<"i">>]], <<"t">>],
47+
<<" shorter. ">>,
48+
[<<"we ">>],
49+
<<"definitely need ">>,
50+
"to compress ",
51+
<<"it.">>
52+
],
53+
% Data from original erlang implementation
54+
ProperlyCompressed =
55+
<<120, 156, 13, 202, 219, 13, 128, 32, 12, 5, 208, 85, 238, 0, 134, 29, 28, 133, 199, 53,
56+
52, 169, 197, 208, 18, 195, 246, 122, 190, 207, 89, 99, 101, 213, 125, 32, 186, 56, 156,
57+
22, 180, 74, 212, 177, 180, 161, 16, 25, 69, 2, 222, 199, 12, 206, 132, 151, 104, 188,
58+
196, 36, 168, 27, 70, 54, 196, 248, 247, 253, 76, 186, 67, 34, 125, 214, 36, 29, 203>>,
59+
ProperlyCompressed = zlib:compress(ToCompressList),
60+
ok.

tests/test.c

+2
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,8 @@ struct Test tests[] = {
323323
TEST_CASE_EXPECTED(test_binary_part, 12),
324324
TEST_CASE(test_binary_split),
325325

326+
TEST_CASE(test_zlib_compress),
327+
326328
TEST_CASE_COND(plusone, 134217728, LONG_MAX != 9223372036854775807),
327329

328330
TEST_CASE_EXPECTED(plusone2, 1),

0 commit comments

Comments
 (0)