|
24 | 24 | #include <math.h>
|
25 | 25 |
|
26 | 26 | #include "atom.h"
|
| 27 | +#include "bitstring.h" |
27 | 28 | #include "defaultatoms.h"
|
28 | 29 | #include "dictionary.h"
|
| 30 | +#include "interop.h" |
29 | 31 | #include "overflow_helpers.h"
|
30 | 32 | #include "term.h"
|
31 | 33 | #include "trace.h"
|
| 34 | +#include "unicode.h" |
32 | 35 | #include "utils.h"
|
33 | 36 |
|
34 | 37 | //Ignore warning caused by gperf generated code
|
@@ -1512,3 +1515,176 @@ term bif_erlang_size_1(Context *ctx, uint32_t fail_label, int live, term arg1)
|
1512 | 1515 |
|
1513 | 1516 | RAISE_ERROR_BIF(fail_label, BADARG_ATOM);
|
1514 | 1517 | }
|
| 1518 | + |
| 1519 | +static term list_to_atom(Context *ctx, term a_list, bool create_new, term *error_reason); |
| 1520 | + |
| 1521 | +term bif_erlang_list_to_atom_1(Context *ctx, uint32_t fail_label, int live, term arg1) |
| 1522 | +{ |
| 1523 | + UNUSED(live); |
| 1524 | + |
| 1525 | + term error_reason; |
| 1526 | + term result = list_to_atom(ctx, arg1, true, &error_reason); |
| 1527 | + if (UNLIKELY(term_is_invalid_term(result))) { |
| 1528 | + RAISE_ERROR_BIF(fail_label, error_reason); |
| 1529 | + } |
| 1530 | + return result; |
| 1531 | +} |
| 1532 | + |
| 1533 | +term bif_erlang_list_to_existing_atom_1(Context *ctx, uint32_t fail_label, int live, term arg1) |
| 1534 | +{ |
| 1535 | + UNUSED(live); |
| 1536 | + |
| 1537 | + term error_reason; |
| 1538 | + term result = list_to_atom(ctx, arg1, false, &error_reason); |
| 1539 | + if (UNLIKELY(term_is_invalid_term(result))) { |
| 1540 | + RAISE_ERROR_BIF(fail_label, error_reason); |
| 1541 | + } |
| 1542 | + return result; |
| 1543 | +} |
| 1544 | + |
| 1545 | +static term list_to_atom(Context *ctx, term a_list, bool create_new, term *error_reason) |
| 1546 | +{ |
| 1547 | + if (UNLIKELY(!term_is_list(a_list))) { |
| 1548 | + *error_reason = BADARG_ATOM; |
| 1549 | + return term_invalid_term(); |
| 1550 | + } |
| 1551 | + |
| 1552 | + int ok; |
| 1553 | + char *atom_string = interop_list_to_utf8_string(a_list, &ok); |
| 1554 | + if (UNLIKELY(!ok)) { |
| 1555 | + *error_reason = OUT_OF_MEMORY_ATOM; |
| 1556 | + return term_invalid_term(); |
| 1557 | + } |
| 1558 | + int atom_string_len = strlen(atom_string); |
| 1559 | + if (UNLIKELY(atom_string_len > 255)) { |
| 1560 | + free(atom_string); |
| 1561 | + *error_reason = SYSTEM_LIMIT_ATOM; |
| 1562 | + return term_invalid_term(); |
| 1563 | + } |
| 1564 | + |
| 1565 | + AtomString atom = malloc(atom_string_len + 1); |
| 1566 | + if (IS_NULL_PTR(atom)) { |
| 1567 | + free(atom_string); |
| 1568 | + *error_reason = OUT_OF_MEMORY_ATOM; |
| 1569 | + return term_invalid_term(); |
| 1570 | + } |
| 1571 | + ((uint8_t *) atom)[0] = atom_string_len; |
| 1572 | + memcpy(((char *) atom) + 1, atom_string, atom_string_len); |
| 1573 | + free(atom_string); |
| 1574 | + |
| 1575 | + enum AtomTableCopyOpt atom_opts = AtomTableCopyAtom; |
| 1576 | + if (!create_new) { |
| 1577 | + atom_opts |= AtomTableAlreadyExisting; |
| 1578 | + } |
| 1579 | + long global_atom_index = atom_table_ensure_atom(ctx->global->atom_table, atom, atom_opts); |
| 1580 | + free((void *) atom); |
| 1581 | + if (UNLIKELY(global_atom_index == ATOM_TABLE_NOT_FOUND)) { |
| 1582 | + *error_reason = BADARG_ATOM; |
| 1583 | + return term_invalid_term(); |
| 1584 | + } else if (UNLIKELY(global_atom_index == ATOM_TABLE_ALLOC_FAIL)) { |
| 1585 | + *error_reason = OUT_OF_MEMORY_ATOM; |
| 1586 | + return term_invalid_term(); |
| 1587 | + } |
| 1588 | + return term_from_atom_index(global_atom_index); |
| 1589 | +} |
| 1590 | + |
| 1591 | +term bif_erlang_binary_to_atom_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2) |
| 1592 | +{ |
| 1593 | + UNUSED(live); |
| 1594 | + |
| 1595 | + term error_reason; |
| 1596 | + term result = binary_to_atom(ctx, arg1, arg2, true, &error_reason); |
| 1597 | + if (UNLIKELY(term_is_invalid_term(result))) { |
| 1598 | + RAISE_ERROR_BIF(fail_label, error_reason); |
| 1599 | + } |
| 1600 | + return result; |
| 1601 | +} |
| 1602 | + |
| 1603 | +term bif_erlang_binary_to_existing_atom_2(Context *ctx, uint32_t fail_label, int live, term arg1, term arg2) |
| 1604 | +{ |
| 1605 | + UNUSED(live); |
| 1606 | + |
| 1607 | + term error_reason; |
| 1608 | + term result = binary_to_atom(ctx, arg1, arg2, false, &error_reason); |
| 1609 | + if (UNLIKELY(term_is_invalid_term(result))) { |
| 1610 | + RAISE_ERROR_BIF(fail_label, error_reason); |
| 1611 | + } |
| 1612 | + return result; |
| 1613 | +} |
| 1614 | + |
| 1615 | +term binary_to_atom(Context *ctx, term a_binary, term encoding, bool create_new, term *error_reason) |
| 1616 | +{ |
| 1617 | + if (UNLIKELY(!term_is_binary(a_binary))) { |
| 1618 | + *error_reason = BADARG_ATOM; |
| 1619 | + return term_invalid_term(); |
| 1620 | + } |
| 1621 | + |
| 1622 | + const char *atom_string = term_binary_data(a_binary); |
| 1623 | + size_t atom_string_len = term_binary_size(a_binary); |
| 1624 | + if (UNLIKELY(atom_string_len > 255)) { |
| 1625 | + *error_reason = SYSTEM_LIMIT_ATOM; |
| 1626 | + return term_invalid_term(); |
| 1627 | + } |
| 1628 | + |
| 1629 | + bool encode_latin1_to_utf8 = false; |
| 1630 | + if (UNLIKELY((encoding == LATIN1_ATOM) |
| 1631 | + && !unicode_buf_is_ascii((const uint8_t *) atom_string, atom_string_len))) { |
| 1632 | + encode_latin1_to_utf8 = true; |
| 1633 | + } else if (UNLIKELY((encoding != LATIN1_ATOM) && (encoding != UNICODE_ATOM) |
| 1634 | + && (encoding != UTF8_ATOM))) { |
| 1635 | + *error_reason = BADARG_ATOM; |
| 1636 | + return term_invalid_term(); |
| 1637 | + } |
| 1638 | + |
| 1639 | + AtomString atom; |
| 1640 | + if (LIKELY(!encode_latin1_to_utf8)) { |
| 1641 | + size_t i = 0; |
| 1642 | + while (i < atom_string_len) { |
| 1643 | + uint32_t codepoint; |
| 1644 | + size_t codepoint_size; |
| 1645 | + if (UNLIKELY(bitstring_utf8_decode( |
| 1646 | + (uint8_t *) atom_string + i, atom_string_len, &codepoint, &codepoint_size)) |
| 1647 | + != UnicodeTransformDecodeSuccess) { |
| 1648 | + *error_reason = BADARG_ATOM; |
| 1649 | + return term_invalid_term(); |
| 1650 | + } |
| 1651 | + i += codepoint_size; |
| 1652 | + } |
| 1653 | + |
| 1654 | + atom = malloc(atom_string_len + 1); |
| 1655 | + ((uint8_t *) atom)[0] = atom_string_len; |
| 1656 | + memcpy(((char *) atom) + 1, atom_string, atom_string_len); |
| 1657 | + } else { |
| 1658 | + // * 2 is the worst case size |
| 1659 | + size_t buf_len = atom_string_len * 2; |
| 1660 | + atom = malloc(buf_len + 1); |
| 1661 | + uint8_t *atom_data = ((uint8_t *) atom) + 1; |
| 1662 | + size_t out_pos = 0; |
| 1663 | + for (size_t i = 0; i < atom_string_len; i++) { |
| 1664 | + size_t out_size; |
| 1665 | + bitstring_utf8_encode(((uint8_t) atom_string[i]), &atom_data[out_pos], &out_size); |
| 1666 | + out_pos += out_size; |
| 1667 | + } |
| 1668 | + if (out_pos > 255) { |
| 1669 | + free((void *) atom); |
| 1670 | + *error_reason = SYSTEM_LIMIT_ATOM; |
| 1671 | + return term_invalid_term(); |
| 1672 | + } |
| 1673 | + ((uint8_t *) atom)[0] = out_pos; |
| 1674 | + } |
| 1675 | + |
| 1676 | + enum AtomTableCopyOpt atom_opts = AtomTableCopyAtom; |
| 1677 | + if (!create_new) { |
| 1678 | + atom_opts |= AtomTableAlreadyExisting; |
| 1679 | + } |
| 1680 | + long global_atom_index = atom_table_ensure_atom(ctx->global->atom_table, atom, atom_opts); |
| 1681 | + free((void *) atom); |
| 1682 | + if (UNLIKELY(global_atom_index == ATOM_TABLE_NOT_FOUND)) { |
| 1683 | + *error_reason = BADARG_ATOM; |
| 1684 | + return term_invalid_term(); |
| 1685 | + } else if (UNLIKELY(global_atom_index == ATOM_TABLE_ALLOC_FAIL)) { |
| 1686 | + *error_reason = OUT_OF_MEMORY_ATOM; |
| 1687 | + return term_invalid_term(); |
| 1688 | + } |
| 1689 | + return term_from_atom_index(global_atom_index); |
| 1690 | +} |
0 commit comments