aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/Makefile.in10
-rw-r--r--erts/emulator/beam/atom.names1
-rw-r--r--erts/emulator/beam/beam_load.c5
-rw-r--r--erts/emulator/beam/bif.c17
-rw-r--r--erts/emulator/beam/bif.tab1
-rw-r--r--erts/emulator/beam/erl_alloc_util.c10
-rwxr-xr-xerts/emulator/beam/erl_bif_info.c4
-rw-r--r--erts/emulator/beam/erl_init.c53
-rw-r--r--erts/emulator/beam/erl_unicode.c63
-rw-r--r--erts/emulator/beam/ops.tab2
-rw-r--r--erts/emulator/beam/packet_parser.c2
-rw-r--r--erts/emulator/beam/sys.h14
-rw-r--r--erts/emulator/drivers/common/efile_drv.c2
-rw-r--r--erts/emulator/sys/common/erl_mseg.c315
-rw-r--r--erts/emulator/sys/common/erl_mseg.h9
-rw-r--r--erts/emulator/sys/common/erl_sys_common_misc.c204
-rw-r--r--erts/emulator/sys/common/erl_util_queue.h77
-rw-r--r--erts/emulator/test/decode_packet_SUITE.erl3
-rw-r--r--erts/emulator/test/num_bif_SUITE.erl22
-rwxr-xr-xerts/emulator/utils/gen_git_version40
20 files changed, 599 insertions, 255 deletions
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 3e44bbb8db..7033ea0a3d 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -20,6 +20,8 @@
include $(ERL_TOP)/make/target.mk
include ../vsn.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
+include $(TARGET)/gen_git_version.mk
+
ENABLE_ALLOC_TYPE_VARS = @ENABLE_ALLOC_TYPE_VARS@
HIPE_ENABLED=@HIPE_ENABLED@
@@ -196,7 +198,7 @@ else
EMU_CC = @EMU_CC@
endif
WFLAGS = @WFLAGS@
-CFLAGS = @STATIC_CFLAGS@ $(TYPE_FLAGS) $(FLAVOR_FLAGS) $(DEFS) $(WFLAGS) $(THR_DEFS) $(ARCHCFLAGS)
+CFLAGS = @STATIC_CFLAGS@ $(TYPE_FLAGS) $(FLAVOR_FLAGS) $(DEFS) $(WFLAGS) $(THR_DEFS) $(ARCHCFLAGS) $(GIT_VSN)
HCC = @HCC@
LD = @LD@
DEXPORT = @DEXPORT@
@@ -1006,6 +1008,12 @@ DEP_FLAGS=-MM $(MG_FLAG) $(CFLAGS) $(INCLUDES) -Idrivers/common -Idrivers/$(ERLA
SYS_SRC=$(ALL_SYS_SRC)
endif
+.PHONY: $(TARGET)/gen_git_version.mk
+$(TARGET)/gen_git_version.mk:
+# We touch beam/erl_bif.info.c if we regenerated the git version to force a
+# rebuild.
+ if $(gen_verbose)utils/gen_git_version $@; then touch beam/erl_bif_info.c; fi
+
.PHONY: depend
ifdef VOID_EMULATOR
depend:
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index f138324e1f..ce60bb9bbc 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -269,6 +269,7 @@ atom hipe_architecture
atom http httph https http_response http_request http_header http_eoh http_error http_bin httph_bin
atom id
atom if_clause
+atom ignore
atom imports
atom in
atom in_exiting
diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c
index f57fa2bfbb..81c1ea749a 100644
--- a/erts/emulator/beam/beam_load.c
+++ b/erts/emulator/beam/beam_load.c
@@ -1853,7 +1853,10 @@ load_code(LoaderState* stp)
unsigned tag;
switch (last_op->a[arg].val) {
- case 0: /* Floating point number */
+ case 0:
+ /* Floating point number.
+ * Not generated by the compiler in R16B and later.
+ */
{
Eterm* hp;
/* XXX:PaN - Halfword should use ARCH_64 variant instead */
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index 46f76624a5..df084e1185 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -2963,15 +2963,14 @@ BIF_RETTYPE float_to_list_2(BIF_ALIST_2)
Eterm* tp = tuple_val(arg);
if (*tp == arity_two && is_small(tp[2])) {
decimals = signed_val(tp[2]);
- if (decimals > 0 && decimals < sizeof(fbuf) - 6 /* "X." ++ "e+YY" */)
- switch (tp[1]) {
- case am_decimals:
- fmt_type = FMT_FIXED;
- continue;
- case am_scientific:
- fmt_type = FMT_SCIENTIFIC;
- continue;
- }
+ switch (tp[1]) {
+ case am_decimals:
+ fmt_type = FMT_FIXED;
+ continue;
+ case am_scientific:
+ fmt_type = FMT_SCIENTIFIC;
+ continue;
+ }
}
}
goto badarg;
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 4aaf466008..e313188901 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -522,6 +522,7 @@ bif erlang:nif_error/2
bif prim_file:internal_name2native/1
bif prim_file:internal_native2name/1
bif prim_file:internal_normalize_utf8/1
+bif prim_file:is_translatable/1
bif file:native_name_encoding/0
#
diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c
index 187bc2b48b..ac7f420708 100644
--- a/erts/emulator/beam/erl_alloc_util.c
+++ b/erts/emulator/beam/erl_alloc_util.c
@@ -643,9 +643,9 @@ alcu_mseg_realloc(Allctr_t *allctr, void *seg, Uint old_size, Uint *new_size_p)
}
static ERTS_INLINE void
-alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size)
+alcu_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags)
{
- erts_mseg_dealloc_opt(allctr->alloc_no, seg, size, &allctr->mseg_opt);
+ erts_mseg_dealloc_opt(allctr->alloc_no, seg, size, flags, &allctr->mseg_opt);
INC_CC(allctr->calls.mseg_dealloc);
}
@@ -2276,7 +2276,7 @@ resize_carrier(Allctr_t *allctr, Block_t *old_blk, Uint umem_sz, UWord flags)
(void *) BLK2UMEM(old_blk),
MIN(new_blk_sz, old_blk_sz) - ABLK_HDR_SZ);
unlink_carrier(&allctr->sbc_list, old_crr);
- alcu_mseg_dealloc(allctr, old_crr, old_crr_sz);
+ alcu_mseg_dealloc(allctr, old_crr, old_crr_sz, ERTS_MSEG_FLG_NONE);
}
else {
/* Old carrier unchanged; restore stat */
@@ -2352,6 +2352,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
Carrier_t *crr;
#if HAVE_ERTS_MSEG
Uint is_mseg = 0;
+ Uint mseg_flags = ERTS_MSEG_FLG_NONE;
#endif
if (IS_SBC_BLK(blk)) {
@@ -2398,6 +2399,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
is_mseg++;
ASSERT(crr_sz % MSEG_UNIT_SZ == 0);
STAT_MSEG_MBC_FREE(allctr, crr_sz);
+ mseg_flags = ERTS_MSEG_FLG_2POW;
}
else
#endif
@@ -2411,7 +2413,7 @@ destroy_carrier(Allctr_t *allctr, Block_t *blk)
#if HAVE_ERTS_MSEG
if (is_mseg) {
- alcu_mseg_dealloc(allctr, crr, crr_sz);
+ alcu_mseg_dealloc(allctr, crr, crr_sz, mseg_flags);
}
else
#endif
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index a601e4fb39..8582a8954b 100755
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -69,7 +69,11 @@ static char erts_system_version[] = ("Erlang " ERLANG_OTP_RELEASE
" [no-c-stack-objects]"
#endif
#ifndef OTP_RELEASE
+#ifdef ERLANG_GIT_VERSION
+ " [source-" ERLANG_GIT_VERSION "]"
+#else
" [source]"
+#endif
#endif
#ifdef ARCH_64
#if HALFWORD_HEAP
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 223c9c4d7e..ec3e0d54cb 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -981,19 +981,64 @@ erl_start(int argc, char **argv)
break;
case 'f':
if (!strncmp(argv[i],"-fn",3)) {
+ int warning_type = ERL_FILENAME_WARNING_WARNING;
arg = get_arg(argv[i]+3, argv[i+1], &i);
switch (*arg) {
case 'u':
- erts_set_user_requested_filename_encoding(ERL_FILENAME_UTF8);
+ switch (*(argv[i]+4)) {
+ case 'w':
+ case 0:
+ break;
+ case 'i':
+ warning_type = ERL_FILENAME_WARNING_IGNORE;
+ break;
+ case 'e':
+ warning_type = ERL_FILENAME_WARNING_ERROR;
+ break;
+ default:
+ erts_fprintf(stderr, "bad type of warnings for "
+ "wrongly coded filename: %s\n", argv[i]+4);
+ erts_usage();
+ }
+ erts_set_user_requested_filename_encoding
+ (
+ ERL_FILENAME_UTF8,
+ warning_type
+ );
break;
case 'l':
- erts_set_user_requested_filename_encoding(ERL_FILENAME_LATIN1);
+ erts_set_user_requested_filename_encoding
+ (
+ ERL_FILENAME_LATIN1,
+ warning_type
+ );
break;
case 'a':
- erts_set_user_requested_filename_encoding(ERL_FILENAME_UNKNOWN);
+ switch (*(argv[i]+4)) {
+ case 'w':
+ case 0:
+ break;
+ case 'i':
+ warning_type = ERL_FILENAME_WARNING_IGNORE;
+ break;
+ case 'e':
+ warning_type = ERL_FILENAME_WARNING_ERROR;
+ break;
+ default:
+ erts_fprintf(stderr, "bad type of warnings for "
+ "wrongly coded filename: %s\n", argv[i]+4);
+ erts_usage();
+ }
+ erts_set_user_requested_filename_encoding
+ (
+ ERL_FILENAME_UNKNOWN,
+ warning_type
+ );
break;
default:
- erts_fprintf(stderr, "bad filename encoding %s, can be (l,u or a)\n", arg);
+ erts_fprintf(stderr, "bad filename encoding %s, can be "
+ "(l,u or a, optionally followed by w, "
+ "i or e)\n", arg);
erts_usage();
}
break;
diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c
index 99108af937..80982f3760 100644
--- a/erts/emulator/beam/erl_unicode.c
+++ b/erts/emulator/beam/erl_unicode.c
@@ -2573,8 +2573,20 @@ BIF_RETTYPE prim_file_internal_native2name_1(BIF_ALIST_1)
case ERL_FILENAME_UTF8:
bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc);
if (erts_analyze_utf8(bytes,size,&err_pos,&num_chars,NULL) != ERTS_UTF8_OK) {
+ Eterm *hp = HAlloc(BIF_P,3);
+ Eterm warn_type = NIL;
erts_free_aligned_binary_bytes(temp_alloc);
- goto noconvert;
+ switch (erts_get_filename_warning_type()) {
+ case ERL_FILENAME_WARNING_IGNORE:
+ warn_type = am_ignore;
+ break;
+ case ERL_FILENAME_WARNING_ERROR:
+ warn_type = am_error;
+ break;
+ default:
+ warn_type = am_warning;
+ }
+ BIF_RET(TUPLE2(hp,am_error,warn_type));
}
num_built = 0;
num_eaten = 0;
@@ -2607,9 +2619,8 @@ BIF_RETTYPE prim_file_internal_native2name_1(BIF_ALIST_1)
erts_free_aligned_binary_bytes(temp_alloc);
BIF_RET(ret);
default:
- goto noconvert;
+ break;
}
- noconvert:
BIF_RET(BIF_ARG_1);
}
@@ -2646,6 +2657,52 @@ BIF_RETTYPE prim_file_internal_normalize_utf8_1(BIF_ALIST_1)
BIF_RET(ret);
}
+BIF_RETTYPE prim_file_is_translatable_1(BIF_ALIST_1)
+{
+ ERTS_DECLARE_DUMMY(Eterm real_bin);
+ ERTS_DECLARE_DUMMY(Uint offset);
+ Uint size;
+ Uint num_chars;
+ Uint bitsize;
+ ERTS_DECLARE_DUMMY(Uint bitoffs);
+ byte *temp_alloc = NULL;
+ byte *bytes;
+ byte *err_pos;
+ int status;
+
+ if (is_not_binary(BIF_ARG_1)) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+ size = binary_size(BIF_ARG_1);
+ ERTS_GET_REAL_BIN(BIF_ARG_1, real_bin, offset, bitoffs, bitsize);
+ if (bitsize != 0) {
+ BIF_ERROR(BIF_P,BADARG);
+ }
+ if (size == 0) {
+ BIF_RET(am_true);
+ }
+
+ /*
+ * If the encoding is latin1, the pathname is always translatable.
+ */
+ switch (erts_get_native_filename_encoding()) {
+ case ERL_FILENAME_LATIN1:
+ BIF_RET(am_true);
+ case ERL_FILENAME_WIN_WCHAR:
+ if (erts_get_user_requested_filename_encoding() == ERL_FILENAME_LATIN1) {
+ BIF_RET(am_true);
+ }
+ }
+
+ /*
+ * Check whether the binary contains legal UTF-8 sequences.
+ */
+ bytes = erts_get_aligned_binary_bytes(BIF_ARG_1, &temp_alloc);
+ status = erts_analyze_utf8(bytes, size, &err_pos, &num_chars, NULL);
+ erts_free_aligned_binary_bytes(temp_alloc);
+ BIF_RET(status == ERTS_UTF8_OK ? am_true : am_false);
+}
+
BIF_RETTYPE file_native_name_encoding_0(BIF_ALIST_0)
{
switch (erts_get_native_filename_encoding()) {
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 6764e88c81..8a79b4910e 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1021,7 +1021,7 @@ bif0 u$bif:erlang:node/0 Dst=d => node Dst
bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => i_get Src Dst
-bif2 Jump=j u$bif:erlang:element/2 S1=s S2=s Dst=d => gen_element(Jump, S1, S2, Dst)
+bif2 Jump=j u$bif:erlang:element/2 S1=s S2=rxy Dst=d => gen_element(Jump, S1, S2, Dst)
bif1 Fail Bif Literal=q Dst => move Literal x | bif1 Fail Bif x Dst
bif1 p Bif S1 Dst => bif1_body Bif S1 Dst
diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c
index 1e301e3593..7c9b2d444a 100644
--- a/erts/emulator/beam/packet_parser.c
+++ b/erts/emulator/beam/packet_parser.c
@@ -67,7 +67,7 @@ static int my_strncasecmp(const char *s1, const char *s2, size_t n)
#define HTTP_HDR_HASH_SIZE 53
#define HTTP_METH_HASH_SIZE 13
-#define HTTP_MAX_NAME_LEN 20
+#define HTTP_MAX_NAME_LEN 50
static char tspecial[128];
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index 249a9c05c2..9416a91480 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -1029,10 +1029,22 @@ char* win32_errorstr(int);
#define ERL_FILENAME_UTF8_MAC (3)
#define ERL_FILENAME_WIN_WCHAR (4)
+/************************************************************************
+ * If a filename in for example list_dir is not in the right encoding, it
+ * will be skipped in the resulting list, but depending on a startup setting
+ * we will inform the user in different ways. These macros define the
+ * different reactions to wrongly coded filenames. In the error case an
+ * exception will be thrown by prim_file.
+ ************************************************************************/
+#define ERL_FILENAME_WARNING_WARNING (0)
+#define ERL_FILENAME_WARNING_IGNORE (1)
+#define ERL_FILENAME_WARNING_ERROR (2)
+
int erts_get_native_filename_encoding(void);
/* The set function is only to be used by erl_init! */
-void erts_set_user_requested_filename_encoding(int encoding);
+void erts_set_user_requested_filename_encoding(int encoding, int warning);
int erts_get_user_requested_filename_encoding(void);
+int erts_get_filename_warning_type(void);
void erts_init_sys_common_misc(void);
diff --git a/erts/emulator/drivers/common/efile_drv.c b/erts/emulator/drivers/common/efile_drv.c
index fda6cf2e53..2279fec72a 100644
--- a/erts/emulator/drivers/common/efile_drv.c
+++ b/erts/emulator/drivers/common/efile_drv.c
@@ -2263,6 +2263,8 @@ file_stop(ErlDrvData e)
desc->fd = FILE_FD_INVALID;
desc->flags = 0;
cq_execute(desc);
+ } else {
+ EF_FREE(desc);
}
} else {
if (desc->fd != FILE_FD_INVALID) {
diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c
index 538eea88d1..bd8ba82a5f 100644
--- a/erts/emulator/sys/common/erl_mseg.c
+++ b/erts/emulator/sys/common/erl_mseg.c
@@ -39,6 +39,7 @@
#include "erl_alloc.h"
#include "big.h"
#include "erl_thr_progress.h"
+#include "erl_util_queue.h"
#if HAVE_ERTS_MSEG
@@ -175,6 +176,7 @@ struct cache_t_ {
Uint size;
void *seg;
cache_t *next;
+ cache_t *prev;
};
@@ -183,9 +185,9 @@ typedef struct ErtsMsegAllctr_t_ ErtsMsegAllctr_t;
struct mem_kind_t {
cache_t cache[MAX_CACHE_SIZE];
- cache_t *cache_unpowered;
- cache_t *cache_area[CACHE_AREAS];
- cache_t *cache_free;
+ cache_t cache_unpowered_node;
+ cache_t cache_powered_node[CACHE_AREAS];
+ cache_t cache_free;
Sint cache_size;
Uint cache_hits;
@@ -516,67 +518,94 @@ do { \
#define ERTS_DBG_MK_CHK_THR_ACCESS(MK)
#endif
-/* NEW CACHE interface */
+/* Cache interface */
-static ERTS_INLINE cache_t *mseg_cache_alloc_descriptor(MemKind *mk) {
- cache_t *c = mk->cache_free;
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (c)
- mk->cache_free = c->next;
-
- return c;
-}
-
-static ERTS_INLINE void mseg_cache_free_descriptor(MemKind *mk, cache_t *c) {
- ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- ASSERT(c);
-
- c->seg = NULL;
+static ERTS_INLINE void mseg_cache_clear_node(cache_t *c) {
+ c->seg = NULL;
c->size = 0;
- c->next = mk->cache_free;
- mk->cache_free = c;
+ c->next = c;
+ c->prev = c;
}
-static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size) {
+static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size, Uint flags) {
cache_t *c;
ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (mk->cache_free && MAP_IS_ALIGNED(seg)) {
- if (IS_2POW(size)) {
+ ASSERT(!MSEG_FLG_IS_2POW(flags) || (MSEG_FLG_IS_2POW(flags) && MAP_IS_ALIGNED(seg) && IS_2POW(size)));
+
+ /* The idea is that sbc caching is prefered over mbc caching.
+ * Blocks are normally allocated in mb carriers and thus cached there.
+ * Large blocks has no such cache and it is up to mseg to cache them to speed things up.
+ */
+
+ if (!erts_circleq_is_empty(&(mk->cache_free))) {
+
+ /* We have free slots, use one to cache the segment */
+
+ c = erts_circleq_head(&(mk->cache_free));
+ erts_circleq_remove(c);
+
+ c->seg = seg;
+ c->size = size;
+
+ if (MSEG_FLG_IS_2POW(flags)) {
int ix = SIZE_TO_CACHE_AREA_IDX(size);
ASSERT(ix < CACHE_AREAS);
ASSERT((1 << (ix + MSEG_ALIGN_BITS)) == size);
- /* unlink from free cache list */
- c = mseg_cache_alloc_descriptor(mk);
+ erts_circleq_push_head(&(mk->cache_powered_node[ix]), c);
- /* link to cache area */
- c->seg = seg;
- c->size = size;
- c->next = mk->cache_area[ix];
+ } else
+ erts_circleq_push_head(&(mk->cache_unpowered_node), c);
- mk->cache_area[ix] = c;
- mk->cache_size++;
+ mk->cache_size++;
+ ASSERT(mk->cache_size <= mk->ma->max_cache_size);
- ASSERT(mk->cache_size <= mk->ma->max_cache_size);
+ return 1;
+ } else if (!MSEG_FLG_IS_2POW(flags) && !erts_circleq_is_empty(&(mk->cache_unpowered_node))) {
- return 1;
- } else {
- /* unlink from free cache list */
- c = mseg_cache_alloc_descriptor(mk);
+ /* No free slots.
+ * Evict oldest slot from unpowered cache so we can cache an unpowered (sbc) segment */
+
+ c = erts_circleq_tail(&(mk->cache_unpowered_node));
+ erts_circleq_remove(c);
+
+ mseg_destroy(mk->ma, mk, c->seg, c->size);
+ mseg_cache_clear_node(c);
+
+ c->seg = seg;
+ c->size = size;
+
+ erts_circleq_push_head(&(mk->cache_unpowered_node), c);
+
+ return 1;
+ } else if (!MSEG_FLG_IS_2POW(flags)) {
+
+ /* No free slots and no unpowered (sbc) slots.
+ * Evict smallest slot from powered cache so we can cache an unpowered (sbc) segment.
+ * Note: Though this is the wanted policy I don't think it is used significantly.
+ * This branch could probably be removed without serious impact.
+ */
+
+ int i;
+
+ for( i = 0; i < CACHE_AREAS; i++) {
+ if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ continue;
+
+ c = erts_circleq_tail(&(mk->cache_powered_node[i]));
+ erts_circleq_remove(c);
+
+ mseg_destroy(mk->ma, mk, c->seg, c->size);
+ mseg_cache_clear_node(c);
- /* link to cache area */
c->seg = seg;
c->size = size;
- c->next = mk->cache_unpowered;
-
- mk->cache_unpowered = c;
- mk->cache_size++;
- ASSERT(mk->cache_size <= mk->ma->max_cache_size);
+ erts_circleq_push_head(&(mk->cache_unpowered_node), c);
return 1;
}
@@ -585,90 +614,110 @@ static ERTS_INLINE int cache_bless_segment(MemKind *mk, void *seg, Uint size) {
return 0;
}
-static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p) {
+static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p, Uint flags) {
Uint size = *size_p;
ERTS_DBG_MK_CHK_THR_ACCESS(mk);
- if (IS_2POW(size)) {
+
+ if (MSEG_FLG_IS_2POW(flags)) {
int i, ix = SIZE_TO_CACHE_AREA_IDX(size);
void *seg;
cache_t *c;
Uint csize;
+ ASSERT(IS_2POW(size));
+
for( i = ix; i < CACHE_AREAS; i++) {
- if ((c = mk->cache_area[i]) == NULL)
+ if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
continue;
+ c = erts_circleq_head(&(mk->cache_powered_node[i]));
+ erts_circleq_remove(c);
+
ASSERT(IS_2POW(c->size));
+ ASSERT(MAP_IS_ALIGNED(c->seg));
+
+ csize = c->size;
+ seg = c->seg;
- /* unlink from cache area */
- csize = c->size;
- seg = c->seg;
- mk->cache_area[i] = c->next;
- c->next = NULL;
mk->cache_size--;
mk->cache_hits++;
/* link to free cache list */
- mseg_cache_free_descriptor(mk, c);
+ mseg_cache_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), c);
ASSERT(!(mk->cache_size < 0));
- /* divvy up the cache - if needed */
- while( i > ix) {
- csize = csize >> 1;
- /* try to cache half of it */
- if (!cache_bless_segment(mk, (char *)seg + csize, csize)) {
- /* wouldn't cache .. destroy it instead */
- mseg_destroy(mk->ma, mk, (char *)seg + csize, csize);
- }
- i--;
+ if (csize != size) {
+ mseg_destroy(mk->ma, mk, (char *)seg + size, csize - size);
}
- ASSERT(csize == size);
+
return seg;
}
}
- else if (mk->cache_unpowered) {
+ else if (!erts_circleq_is_empty(&(mk->cache_unpowered_node))) {
void *seg;
- cache_t *c, *pc;
+ cache_t *c;
+ cache_t *best = NULL;
+ Uint bdiff = 0;
Uint csize;
Uint bad_max_abs = mk->ma->abs_max_cache_bad_fit;
Uint bad_max_rel = mk->ma->rel_max_cache_bad_fit;
- c = mk->cache_unpowered;
- pc = c;
-
- while (c) {
+ erts_circleq_foreach(c, &(mk->cache_unpowered_node)) {
csize = c->size;
- if (csize >= size &&
- ((csize - size)*100 < bad_max_rel*size) &&
- (csize - size) < bad_max_abs ) {
+ if (csize >= size) {
+ if (((csize - size)*100 < bad_max_rel*size) && (csize - size) < bad_max_abs ) {
- /* unlink from cache area */
- seg = c->seg;
+ seg = c->seg;
- if (pc == c) {
- mk->cache_unpowered = c->next;
- } else {
- pc->next = c->next;
- }
+ erts_circleq_remove(c);
+
+ mk->cache_size--;
+ mk->cache_hits++;
- c->next = NULL;
- mk->cache_size--;
- mk->cache_hits++;
+ mseg_cache_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), c);
- /* link to free cache list */
- mseg_cache_free_descriptor(mk, c);
- *size_p = csize;
+ *size_p = csize;
- return seg;
+ return seg;
+
+ } else if (!best || (csize - size) < bdiff) {
+ best = c;
+ bdiff = csize - size;
+ }
}
+ }
+
+ /* No cached segment met our criteria, use the best one found and trim it */
+
+ if (best) {
+
+ seg = best->seg;
+ csize = best->size;
+
+ ASSERT(best->seg);
+ ASSERT(best->size > 0);
+
+ mk->cache_hits++;
+
+ /* Use current cache placement for remaining segment space */
+
+ best->seg = seg + size;
+ best->size = csize - size;
+
+ ASSERT((size % GET_PAGE_SIZE) == 0);
+ ASSERT((best->size % GET_PAGE_SIZE) == 0);
+
+ *size_p = size;
+
+ return seg;
- pc = c;
- c = c->next;
}
}
return NULL;
@@ -679,20 +728,18 @@ static ERTS_INLINE void *cache_get_segment(MemKind *mk, Uint *size_p) {
* using callbacks from aux-work in the scheduler.
*/
-static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t **head) {
+static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t *head) {
cache_t *c = NULL;
- c = *head;
-
- ASSERT( c != NULL );
-
- *head = c->next;
+ c = erts_circleq_tail(head);
+ erts_circleq_remove(c);
if (erts_mtrace_enabled)
erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg);
mseg_destroy(mk->ma, mk, c->seg, c->size);
- mseg_cache_free_descriptor(mk, c);
+ mseg_cache_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), c);
mk->segments.current.watermark--;
mk->cache_size--;
@@ -702,30 +749,27 @@ static ERTS_INLINE Uint mseg_drop_one_memkind_cache_size(MemKind *mk, cache_t **
return mk->cache_size;
}
-static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, cache_t **head) {
- cache_t *c = NULL, *next = NULL;
-
- c = *head;
- ASSERT( c != NULL );
+static ERTS_INLINE Uint mseg_drop_memkind_cache_size(MemKind *mk, cache_t *head) {
+ cache_t *c = NULL;
- while (c) {
+ while (!erts_circleq_is_empty(head)) {
- next = c->next;
+ c = erts_circleq_tail(head);
+ erts_circleq_remove(c);
if (erts_mtrace_enabled)
erts_mtrace_crr_free(SEGTYPE, SEGTYPE, c->seg);
mseg_destroy(mk->ma, mk, c->seg, c->size);
- mseg_cache_free_descriptor(mk, c);
+
+ mseg_cache_clear_node(c);
+ erts_circleq_push_head(&(mk->cache_free), c);
mk->segments.current.watermark--;
mk->cache_size--;
- c = next;
}
- *head = NULL;
-
ASSERT( mk->cache_size >= 0 );
return mk->cache_size;
@@ -743,12 +787,12 @@ static Uint mseg_check_memkind_cache(MemKind *mk) {
ERTS_DBG_MK_CHK_THR_ACCESS(mk);
for (i = 0; i < CACHE_AREAS; i++) {
- if (mk->cache_area[i] != NULL)
- return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_area[i]));
+ if (!erts_circleq_is_empty(&(mk->cache_powered_node[i])))
+ return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_powered_node[i]));
}
- if (mk->cache_unpowered)
- return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_unpowered));
+ if (!erts_circleq_is_empty(&(mk->cache_unpowered_node)))
+ return mseg_drop_one_memkind_cache_size(mk, &(mk->cache_unpowered_node));
return 0;
}
@@ -804,17 +848,17 @@ static void mseg_clear_memkind_cache(MemKind *mk) {
/* drop pow2 caches */
for (i = 0; i < CACHE_AREAS; i++) {
- if (mk->cache_area[i] == NULL)
+ if (erts_circleq_is_empty(&(mk->cache_powered_node[i])))
continue;
- mseg_drop_memkind_cache_size(mk, &(mk->cache_area[i]));
- ASSERT(mk->cache_area[i] == NULL);
+ mseg_drop_memkind_cache_size(mk, &(mk->cache_powered_node[i]));
+ ASSERT(erts_circleq_is_empty(&(mk->cache_powered_node[i])));
}
/* drop varied caches */
- if(mk->cache_unpowered)
- mseg_drop_memkind_cache_size(mk, &(mk->cache_unpowered));
+ if (!erts_circleq_is_empty(&(mk->cache_unpowered_node)))
+ mseg_drop_memkind_cache_size(mk, &(mk->cache_unpowered_node));
- ASSERT(mk->cache_unpowered == NULL);
+ ASSERT(erts_circleq_is_empty(&(mk->cache_unpowered_node)));
ASSERT(mk->cache_size == 0);
}
@@ -873,7 +917,7 @@ mseg_alloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, Uint *size_p,
ma->min_seg_size = size;
#endif
- if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size)) != NULL)
+ if (opt->cache && mk->cache_size > 0 && (seg = cache_get_segment(mk, &size, flags)) != NULL)
goto done;
if ((seg = mseg_create(ma, mk, size)) == NULL)
@@ -894,14 +938,13 @@ done:
static void
mseg_dealloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg, Uint size,
- const ErtsMsegOpt_t *opt)
+ Uint flags, const ErtsMsegOpt_t *opt)
{
MemKind* mk = memkind(ma, opt);
-
ERTS_MSEG_DEALLOC_STAT(mk,size);
- if (opt->cache && cache_bless_segment(mk, seg, size)) {
+ if (opt->cache && cache_bless_segment(mk, seg, size, flags)) {
schedule_cache_check(ma);
goto done;
}
@@ -934,7 +977,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
/* Dealloc old segment if new segment is of size 0 */
if (!(*new_size_p)) {
- mseg_dealloc(ma, atype, seg, old_size, opt);
+ mseg_dealloc(ma, atype, seg, old_size, flags, opt);
DEC_CC(ma, dealloc);
return NULL;
}
@@ -993,7 +1036,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
else {
if (!opt->preserv) {
- mseg_dealloc(ma, atype, seg, old_size, opt);
+ mseg_dealloc(ma, atype, seg, old_size, flags, opt);
new_seg = mseg_alloc(ma, atype, &new_size, flags, opt);
ASSERT(MAP_IS_ALIGNED(new_seg) || !new_seg);
}
@@ -1020,7 +1063,7 @@ mseg_realloc(ErtsMsegAllctr_t *ma, ErtsAlcType_t atype, void *seg,
new_size = old_size;
else {
sys_memcpy(((char *) new_seg), ((char *) seg), MIN(new_size, old_size));
- mseg_dealloc(ma, atype, seg, old_size, opt);
+ mseg_dealloc(ma, atype, seg, old_size, flags, opt);
}
#endif
}
@@ -1497,19 +1540,19 @@ erts_mseg_alloc(ErtsAlcType_t atype, Uint *size_p, Uint flags)
void
erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg,
- Uint size, const ErtsMsegOpt_t *opt)
+ Uint size, Uint flags, const ErtsMsegOpt_t *opt)
{
ErtsMsegAllctr_t *ma = ERTS_MSEG_ALLCTR_OPT(opt);
ERTS_MSEG_LOCK(ma);
ERTS_DBG_MA_CHK_THR_ACCESS(ma);
- mseg_dealloc(ma, atype, seg, size, opt);
+ mseg_dealloc(ma, atype, seg, size, flags, opt);
ERTS_MSEG_UNLOCK(ma);
}
void
-erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size)
+erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size, Uint flags)
{
- erts_mseg_dealloc_opt(atype, seg, size, &erts_mseg_default_opt);
+ erts_mseg_dealloc_opt(atype, seg, size, flags, &erts_mseg_default_opt);
}
void *
@@ -1556,27 +1599,28 @@ erts_mseg_unit_size(void)
return MSEG_ALIGNED_SIZE;
}
+
static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name)
{
int i;
+ /* Clear all cache headers */
+ mseg_cache_clear_node(&(mk->cache_free));
+ mseg_cache_clear_node(&(mk->cache_unpowered_node));
+
for (i = 0; i < CACHE_AREAS; i++) {
- mk->cache_area[i] = NULL;
+ mseg_cache_clear_node(&(mk->cache_powered_node[i]));
}
- mk->cache_free = NULL;
+ /* Populate cache free list */
ASSERT(ma->max_cache_size <= MAX_CACHE_SIZE);
for (i = 0; i < ma->max_cache_size; i++) {
- mk->cache[i].seg = NULL;
- mk->cache[i].size = 0;
- mk->cache[i].next = mk->cache_free;
- mk->cache_free = &(mk->cache[i]);
+ mseg_cache_clear_node(&(mk->cache[i]));
+ erts_circleq_push_head(&(mk->cache_free), &(mk->cache[i]));
}
- mk->cache_unpowered = NULL;
-
mk->cache_size = 0;
mk->cache_hits = 0;
@@ -1594,9 +1638,6 @@ static void mem_kind_init(ErtsMsegAllctr_t *ma, MemKind* mk, const char* name)
ma->mk_list = mk;
}
-
-
-
void
erts_mseg_init(ErtsMsegInit_t *init)
{
@@ -1721,7 +1762,7 @@ erts_mseg_test(unsigned long op,
case 0x401:
return (unsigned long) erts_mseg_alloc(ERTS_ALC_A_INVALID, (Uint *) a1, (Uint) 0);
case 0x402:
- erts_mseg_dealloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2);
+ erts_mseg_dealloc(ERTS_ALC_A_INVALID, (void *) a1, (Uint) a2, (Uint) 0);
return (unsigned long) 0;
case 0x403:
return (unsigned long) erts_mseg_realloc(ERTS_ALC_A_INVALID,
diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h
index 3d0b0f0355..3cab9e18da 100644
--- a/erts/emulator/sys/common/erl_mseg.h
+++ b/erts/emulator/sys/common/erl_mseg.h
@@ -74,7 +74,7 @@ typedef struct {
{ \
4*1024*1024, /* amcbf: Absolute max cache bad fit */ \
20, /* rmcbf: Relative max cache bad fit */ \
- 5, /* mcs: Max cache size */ \
+ 10, /* mcs: Max cache size */ \
1000 /* cci: Cache check interval */ \
}
@@ -93,11 +93,10 @@ extern const ErtsMsegOpt_t erts_mseg_default_opt;
void *erts_mseg_alloc(ErtsAlcType_t, Uint *, Uint);
void *erts_mseg_alloc_opt(ErtsAlcType_t, Uint *, Uint, const ErtsMsegOpt_t *);
-void erts_mseg_dealloc(ErtsAlcType_t, void *, Uint);
-void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, Uint, const ErtsMsegOpt_t *);
+void erts_mseg_dealloc(ErtsAlcType_t, void *, Uint, Uint);
+void erts_mseg_dealloc_opt(ErtsAlcType_t, void *, Uint, Uint, const ErtsMsegOpt_t *);
void *erts_mseg_realloc(ErtsAlcType_t, void *, Uint, Uint *, Uint);
-void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, Uint, Uint *,
- Uint, const ErtsMsegOpt_t *);
+void *erts_mseg_realloc_opt(ErtsAlcType_t, void *, Uint, Uint *, Uint, const ErtsMsegOpt_t *);
void erts_mseg_clear_cache(void);
void erts_mseg_cache_check(void);
Uint erts_mseg_no( const ErtsMsegOpt_t *);
diff --git a/erts/emulator/sys/common/erl_sys_common_misc.c b/erts/emulator/sys/common/erl_sys_common_misc.c
index 1bf5fa89f4..0b31c125e5 100644
--- a/erts/emulator/sys/common/erl_sys_common_misc.c
+++ b/erts/emulator/sys/common/erl_sys_common_misc.c
@@ -47,14 +47,16 @@
/* Written once and only once */
static int filename_encoding = ERL_FILENAME_UNKNOWN;
+static int filename_warning = ERL_FILENAME_WARNING_WARNING;
#if defined(__WIN32__) || defined(__DARWIN__)
static int user_filename_encoding = ERL_FILENAME_UTF8; /* Default unicode on windows */
#else
static int user_filename_encoding = ERL_FILENAME_LATIN1;
#endif
-void erts_set_user_requested_filename_encoding(int encoding)
+void erts_set_user_requested_filename_encoding(int encoding, int warning)
{
user_filename_encoding = encoding;
+ filename_warning = warning;
}
int erts_get_user_requested_filename_encoding(void)
@@ -62,6 +64,11 @@ int erts_get_user_requested_filename_encoding(void)
return user_filename_encoding;
}
+int erts_get_filename_warning_type(void)
+{
+ return filename_warning;
+}
+
void erts_init_sys_common_misc(void)
{
#if defined(__WIN32__)
@@ -107,9 +114,9 @@ int erts_get_native_filename_encoding(void)
}
/* For internal use by sys_double_to_chars_fast() */
-static char* float_first_trailing_zero(char* p)
+static char* find_first_trailing_zero(char* p)
{
- for (--p; *p == '0' && *(p-1) == '0'; --p);
+ for (; *(p-1) == '0'; --p);
if (*(p-1) == '.') ++p;
return p;
}
@@ -120,34 +127,83 @@ sys_double_to_chars(double fp, char *buffer, size_t buffer_size)
return sys_double_to_chars_ext(fp, buffer, buffer_size, SYS_DEFAULT_FLOAT_DECIMALS);
}
+/* Convert float to string using fixed point notation.
+ * decimals must be >= 0
+ * if compact != 0, the trailing 0's will be truncated
+ */
int
-sys_double_to_chars_fast(double f, char *outbuf, int maxlen, int decimals, int compact)
+sys_double_to_chars_fast(double f, char *buffer, int buffer_size, int decimals,
+ int compact)
{
- enum {
- FRAC_SIZE = 52
- , EXP_SIZE = 11
- , EXP_MASK = (1ll << EXP_SIZE) - 1
- , FRAC_MASK = (1ll << FRAC_SIZE) - 1
- , FRAC_MASK2 = (1ll << (FRAC_SIZE + 1)) - 1
- , MAX_FLOAT = 1ll << (FRAC_SIZE+1)
+ /* Note that some C compilers don't support "static const" propagation
+ * so we use a defines */
+ #define SYS_DOUBLE_RND_CONST 0.55555555555555555
+ #define FRAC_SIZE 52
+ #define EXP_SIZE 11
+ #define EXP_MASK ((1ll << EXP_SIZE) - 1)
+ #define MAX_DECIMALS (sizeof(cs_sys_double_pow10) \
+ / sizeof(cs_sys_double_pow10[0]))
+ #define FRAC_MASK ((1ll << FRAC_SIZE) - 1)
+ #define FRAC_MASK2 ((1ll << (FRAC_SIZE + 1)) - 1)
+ #define MAX_FLOAT (1ll << (FRAC_SIZE+1))
+
+ static const double cs_sys_double_pow10[] = {
+ SYS_DOUBLE_RND_CONST / 1ll,
+ SYS_DOUBLE_RND_CONST / 10ll,
+ SYS_DOUBLE_RND_CONST / 100ll,
+ SYS_DOUBLE_RND_CONST / 1000ll,
+ SYS_DOUBLE_RND_CONST / 10000ll,
+ SYS_DOUBLE_RND_CONST / 100000ll,
+ SYS_DOUBLE_RND_CONST / 1000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000000000ll,
+ SYS_DOUBLE_RND_CONST / 10000000000000000ll,
+ SYS_DOUBLE_RND_CONST / 100000000000000000ll,
+ SYS_DOUBLE_RND_CONST / 1000000000000000000ll
};
- long long mantissa, int_part, int_part2, frac_part;
+ long long mantissa, int_part = 0, frac_part = 0;
short exp;
- int sign, i, n, m, max;
- double absf;
+ int max;
+ int neg;
+ double fr;
union { long long L; double F; } x;
- char c, *p = outbuf;
- int digit, roundup;
+ char *p = buffer;
+
+ if (decimals < 0)
+ return -1;
- x.F = f;
+ /* Round the number to given decimal places. The number of 5's in the
+ * SYS_DOUBLE_RND_CONST constant is chosen such that adding any more 5's doesn't
+ * change the double precision of the number, i.e.:
+ * 1> term_to_binary(0.55555555555555555, [{minor_version, 1}]).
+ * <<131,70,63,225,199,28,113,199,28,114>>
+ * 2> term_to_binary(0.5555555555555555555, [{minor_version, 1}]).
+ * <<131,70,63,225,199,28,113,199,28,114>>
+ */
+ if (f >= 0) {
+ neg = 0;
+ fr = decimals < MAX_DECIMALS ? (f + cs_sys_double_pow10[decimals]) : f;
+ x.F = fr;
+ } else {
+ neg = 1;
+ fr = decimals < MAX_DECIMALS ? (f - cs_sys_double_pow10[decimals]) : f;
+ x.F = -fr;
+ }
exp = (x.L >> FRAC_SIZE) & EXP_MASK;
mantissa = x.L & FRAC_MASK;
- sign = x.L >= 0 ? 1 : -1;
+
if (exp == EXP_MASK) {
if (mantissa == 0) {
- if (sign == -1)
+ if (neg)
*p++ = '-';
*p++ = 'i';
*p++ = 'n';
@@ -158,101 +214,79 @@ sys_double_to_chars_fast(double f, char *outbuf, int maxlen, int decimals, int c
*p++ = 'n';
}
*p = '\0';
- return p - outbuf;
+ return p - buffer;
}
exp -= EXP_MASK >> 1;
mantissa |= (1ll << FRAC_SIZE);
- frac_part = 0;
- int_part = 0;
- absf = f * sign;
-
- /* Don't bother with optimizing too large numbers and decimals */
- if (absf > MAX_FLOAT || decimals > maxlen-17) {
- int len = erts_snprintf(outbuf, maxlen, "%.*f", decimals, f);
- if (len >= maxlen)
+
+ /* Don't bother with optimizing too large numbers or too large precision */
+ if (x.F > MAX_FLOAT || decimals >= MAX_DECIMALS) {
+ int len = erts_snprintf(buffer, buffer_size, "%.*f", decimals, f);
+ char* p = buffer + len;
+ if (len >= buffer_size)
return -1;
- p = outbuf + len;
/* Delete trailing zeroes */
if (compact)
- p = float_first_trailing_zero(outbuf + len);
+ p = find_first_trailing_zero(p);
*p = '\0';
- return p - outbuf;
- }
-
- if (exp >= FRAC_SIZE)
+ return p - buffer;
+ } else if (exp >= FRAC_SIZE) {
int_part = mantissa << (exp - FRAC_SIZE);
- else if (exp >= 0) {
+ } else if (exp >= 0) {
int_part = mantissa >> (FRAC_SIZE - exp);
frac_part = (mantissa << (exp + 1)) & FRAC_MASK2;
- }
- else /* if (exp < 0) */
+ } else /* if (exp < 0) */ {
frac_part = (mantissa & FRAC_MASK2) >> -(exp + 1);
+ }
- if (int_part == 0) {
- if (sign == -1)
+ if (!int_part) {
+ if (neg)
*p++ = '-';
*p++ = '0';
} else {
- int ret;
+ int ret, i, n;
while (int_part != 0) {
- int_part2 = int_part / 10;
- *p++ = (char)(int_part - ((int_part2 << 3) + (int_part2 << 1)) + '0');
- int_part = int_part2;
+ long long j = int_part / 10;
+ *p++ = (char)(int_part - ((j << 3) + (j << 1)) + '0');
+ int_part = j;
}
- if (sign == -1)
+ if (neg)
*p++ = '-';
/* Reverse string */
- ret = p - outbuf;
+ ret = p - buffer;
for (i = 0, n = ret/2; i < n; i++) {
- int j = ret - i - 1;
- c = outbuf[i];
- outbuf[i] = outbuf[j];
- outbuf[j] = c;
+ int j = ret - i - 1;
+ char c = buffer[i];
+ buffer[i] = buffer[j];
+ buffer[j] = c;
}
}
- if (decimals != 0)
+
+ if (decimals > 0) {
+ int i;
*p++ = '.';
- max = maxlen - (p - outbuf) - 1 /* leave room for trailing '\0' */;
- if (max > decimals)
+ max = buffer_size - (p - buffer) - 1 /* leave room for trailing '\0' */;
+
+ if (decimals > max)
+ return -1; /* the number is not large enough to fit in the buffer */
+
max = decimals;
- for (m = 0; m < max; m++) {
- /* frac_part *= 10; */
- frac_part = (frac_part << 3) + (frac_part << 1);
- *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
- frac_part &= FRAC_MASK2;
- }
+ for (i = 0; i < max; i++) {
+ /* frac_part *= 10; */
+ frac_part = (frac_part << 3) + (frac_part << 1);
- roundup = 0;
- /* Rounding - look at the next digit */
- frac_part = (frac_part << 3) + (frac_part << 1);
- digit = (frac_part >> (FRAC_SIZE + 1));
- if (digit > 5)
- roundup = 1;
- else if (digit == 5) {
- frac_part &= FRAC_MASK2;
- if (frac_part != 0) roundup = 1;
- }
- if (roundup) {
- char d;
- int pos = p - outbuf - 1;
- do {
- d = outbuf[pos];
- if (d == '-') break;
- if (d == '.') continue;
- if (++d != ':') {
- outbuf[pos] = d;
- break;
- }
- outbuf[pos] = '0';
- } while (--pos);
+ *p++ = (char)((frac_part >> (FRAC_SIZE + 1)) + '0');
+ frac_part &= FRAC_MASK2;
+ }
+
+ /* Delete trailing zeroes */
+ if (compact)
+ p = find_first_trailing_zero(p);
}
- /* Delete trailing zeroes */
- if (compact && *(p - 1) == '0')
- p = float_first_trailing_zero(--p);
*p = '\0';
- return p - outbuf;
+ return p - buffer;
}
diff --git a/erts/emulator/sys/common/erl_util_queue.h b/erts/emulator/sys/common/erl_util_queue.h
new file mode 100644
index 0000000000..47925e2264
--- /dev/null
+++ b/erts/emulator/sys/common/erl_util_queue.h
@@ -0,0 +1,77 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2013. 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%
+ */
+
+#ifndef ERL_UTIL_QUEUE_H_
+#define ERL_UTIL_QUEUE_H_
+
+#define erts_circleq_head(Q) ((Q)->next)
+#define erts_circleq_tail(Q) ((Q)->prev)
+#define erts_circleq_next(Q) ((Q)->next)
+#define erts_circleq_prev(Q) ((Q)->prev)
+#define erts_circleq_is_empty(Q) ((Q)->next == (void *)(Q))
+
+#define erts_circleq_remove(N) \
+ do { \
+ (N)->next->prev = (N)->prev; \
+ (N)->prev->next = (N)->next; \
+ (N)->next = (N); \
+ (N)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_pop_head(Q, N) \
+ do { \
+ (N) = (Q)->next; \
+ (N)->next->prev = (N)->prev; \
+ (N)->prev->next = (N)->next; \
+ (N)->next = (N); \
+ (N)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_pop_tail(Q, N) \
+ do { \
+ (N) = (Q)->prev; \
+ (N)->next->prev = (N)->prev; \
+ (N)->prev->next = (N)->next; \
+ (N)->next = (N); \
+ (N)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_push_head(Q, N) \
+ do { \
+ (N)->next = (Q)->next; \
+ (N)->prev = (void *)(Q); \
+ (Q)->next->prev = (N); \
+ (Q)->next = (N); \
+ } while(0)
+
+#define erts_circleq_push_tail(Q, N) \
+ do { \
+ (N)->prev = (Q)->prev; \
+ (N)->next = (void *)(Q); \
+ (Q)->prev->next = (N); \
+ (Q)->prev = (N); \
+ } while(0)
+
+#define erts_circleq_foreach(V, Q) \
+ for ((V) = (Q)->next; (V) != (const void *)(Q); (V) = (V)->next)
+
+#define erts_circleq_foreach_reverse(V, Q) \
+ for ((V) = (Q)->prev; (V) != (const void *)(Q); (V) = (V)->prev)
+
+#endif
diff --git a/erts/emulator/test/decode_packet_SUITE.erl b/erts/emulator/test/decode_packet_SUITE.erl
index 4acbe8c6e0..1714551e15 100644
--- a/erts/emulator/test/decode_packet_SUITE.erl
+++ b/erts/emulator/test/decode_packet_SUITE.erl
@@ -403,6 +403,9 @@ http_request(Msg) ->
{"Other-Field: with some text\r\n",
{http_header,0, "Other-Field" ,undefined, "with some text"},
{http_header,0,<<"Other-Field">>,undefined,<<"with some text">>}},
+ {"Make-sure-a-LONG-HEaDer-fIeLd-is-fORMATTED-NicelY: with some text\r\n",
+ {http_header,0, "Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely" ,undefined, "with some text"},
+ {http_header,0,<<"Make-Sure-A-Long-Header-Field-Is-Formatted-Nicely">>,undefined,<<"with some text">>}},
{"Multi-Line: Once upon a time in a land far far away,\r\n"
" there lived a princess imprisoned in the highest tower\r\n"
" of the most haunted castle.\r\n",
diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl
index bf33c337ee..c625e655ce 100644
--- a/erts/emulator/test/num_bif_SUITE.erl
+++ b/erts/emulator/test/num_bif_SUITE.erl
@@ -128,10 +128,17 @@ t_float_to_list(Config) when is_list(Config) ->
"-1.00000000000000000000e+00" = float_to_list(-1.0, []),
"-1.00000000000000000000" = float_to_list(-1.0, [{decimals, 20}]),
{'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, -1}])),
- {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, 250}])),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{decimals, 254}])),
+ {'EXIT', {badarg, _}} = (catch float_to_list(1.0, [{scientific, 250}])),
{'EXIT', {badarg, _}} = (catch float_to_list(1.0e+300, [{decimals, 1}])),
"1.0e+300" = float_to_list(1.0e+300, [{scientific, 1}]),
- "1.0" = float_to_list(1.0, [{decimals, 249}, compact]),
+ "1.0" = float_to_list(1.0, [{decimals, 249}, compact]),
+ "1" = float_to_list(1.0, [{decimals, 0}]),
+ "2" = float_to_list(1.9, [{decimals, 0}]),
+ "123456789012345680.0" = erlang:float_to_list(
+ 123456789012345678.0, [{decimals, 236}, compact]),
+ {'EXIT', {badarg, _}} = (catch float_to_list(
+ 123456789012345678.0, [{decimals, 237}])),
Expected = "1." ++ string:copies("0", 249) ++ "e+00",
Expected = float_to_list(1.0, [{scientific, 249}, compact]),
@@ -139,7 +146,8 @@ t_float_to_list(Config) when is_list(Config) ->
X2 = float_to_list(1.0, [{scientific, 20}]),
X1 = X2,
"1.000e+00" = float_to_list(1.0, [{scientific, 3}]),
- "1.000" = float_to_list(1.0, [{decimals, 3}]),
+ "1.000" = float_to_list(1.0, [{decimals, 3}]),
+ "1.0" = float_to_list(1.0, [{decimals, 1}]),
"1.0" = float_to_list(1.0, [{decimals, 3}, compact]),
"1.12" = float_to_list(1.123, [{decimals, 2}]),
"1.123" = float_to_list(1.123, [{decimals, 3}]),
@@ -148,6 +156,14 @@ t_float_to_list(Config) when is_list(Config) ->
"1.12300" = float_to_list(1.123, [{decimals, 5}]),
"1.123" = float_to_list(1.123, [{decimals, 5}, compact]),
"1.1234" = float_to_list(1.1234,[{decimals, 6}, compact]),
+ "1.01" = float_to_list(1.005, [{decimals, 2}]),
+ "-1.01" = float_to_list(-1.005,[{decimals, 2}]),
+ "0.999" = float_to_list(0.999, [{decimals, 3}]),
+ "-0.999" = float_to_list(-0.999,[{decimals, 3}]),
+ "1.0" = float_to_list(0.999, [{decimals, 2}, compact]),
+ "-1.0" = float_to_list(-0.999,[{decimals, 2}, compact]),
+ "0.5" = float_to_list(0.5, [{decimals, 1}]),
+ "-0.5" = float_to_list(-0.5, [{decimals, 1}]),
"2.333333" = erlang:float_to_list(7/3, [{decimals, 6}, compact]),
"2.333333" = erlang:float_to_list(7/3, [{decimals, 6}]),
"0.00000000000000000000e+00" = float_to_list(0.0, [compact]),
diff --git a/erts/emulator/utils/gen_git_version b/erts/emulator/utils/gen_git_version
new file mode 100755
index 0000000000..d93a97cbbb
--- /dev/null
+++ b/erts/emulator/utils/gen_git_version
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+OUTPUT_FILE=$1
+
+if command -v git 2>&1 >/dev/null &&
+ test -d $ERL_TOP/.git -o -f $ERL_TOP/.git
+then
+ VSN=`git describe --match "OTP_R[0-9][0-9][A-B]*" HEAD`
+ case "$VSN" in
+ OTP_R*-g*)
+ VSN=`echo $VSN | sed -e 's/.*-g\\(.*\\)/\\1/g'` ;;
+ *) VSN="na" ;;
+ esac
+else
+ VSN="na"
+fi
+
+
+# Only update the file if there has been a change to
+# the version number.
+if test -r $OUTPUT_FILE
+then
+ VC=`sed -n -e 's/^.*"\\\\"\\(.*\\)\\\\"".*/\\1/p' < $OUTPUT_FILE`
+else
+ VC=unset
+fi
+echo "VSN = $VSN"
+echo "VC = $VC"
+if test "$VSN" != "$VC"
+then
+ echo "# Automatically generated by $0 - DO NOT EDIT." > $OUTPUT_FILE
+ if test "$VSN" = "na"
+ then
+ echo "# GIT_VSN=-DERLANG_GIT_VERSION=\"\\\"$VSN\\\"\"" >> $OUTPUT_FILE
+ else
+ echo "GIT_VSN=-DERLANG_GIT_VERSION=\"\\\"$VSN\\\"\"" >> $OUTPUT_FILE
+ fi
+ exit 0
+fi
+exit 1 \ No newline at end of file