| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| /* Various JS utility functions. */ |
| |
| #include "jsutil.h" |
| |
| #include "mozilla/Assertions.h" |
| #include "mozilla/PodOperations.h" |
| |
| #include <stdio.h> |
| #include "jstypes.h" |
| |
| #ifdef WIN32 |
| # include "jswin.h" |
| #endif |
| |
| #include "js/Utility.h" |
| |
| #ifdef USE_ZLIB |
| #include "zlib.h" |
| #endif |
| |
| using namespace js; |
| |
| using mozilla::PodArrayZero; |
| |
| #if USE_ZLIB |
| static void * |
| zlib_alloc(void *cx, uInt items, uInt size) |
| { |
| return js_calloc(items, size); |
| } |
| |
| static void |
| zlib_free(void *cx, void *addr) |
| { |
| js_free(addr); |
| } |
| |
| Compressor::Compressor(const unsigned char *inp, size_t inplen) |
| : inp(inp), |
| inplen(inplen), |
| outbytes(0) |
| { |
| JS_ASSERT(inplen > 0); |
| zs.opaque = NULL; |
| zs.next_in = (Bytef *)inp; |
| zs.avail_in = 0; |
| zs.next_out = NULL; |
| zs.avail_out = 0; |
| zs.zalloc = zlib_alloc; |
| zs.zfree = zlib_free; |
| } |
| |
| |
| Compressor::~Compressor() |
| { |
| int ret = deflateEnd(&zs); |
| if (ret != Z_OK) { |
| // If we finished early, we can get a Z_DATA_ERROR. |
| JS_ASSERT(ret == Z_DATA_ERROR); |
| JS_ASSERT(uInt(zs.next_in - inp) < inplen || !zs.avail_out); |
| } |
| } |
| |
| bool |
| Compressor::init() |
| { |
| if (inplen >= UINT32_MAX) |
| return false; |
| // zlib is slow and we'd rather be done compression sooner |
| // even if it means decompression is slower which penalizes |
| // Function.toString() |
| int ret = deflateInit(&zs, Z_BEST_SPEED); |
| if (ret != Z_OK) { |
| JS_ASSERT(ret == Z_MEM_ERROR); |
| return false; |
| } |
| return true; |
| } |
| |
| void |
| Compressor::setOutput(unsigned char *out, size_t outlen) |
| { |
| JS_ASSERT(outlen > outbytes); |
| zs.next_out = out + outbytes; |
| zs.avail_out = outlen - outbytes; |
| } |
| |
| Compressor::Status |
| Compressor::compressMore() |
| { |
| JS_ASSERT(zs.next_out); |
| uInt left = inplen - (zs.next_in - inp); |
| bool done = left <= CHUNKSIZE; |
| if (done) |
| zs.avail_in = left; |
| else if (zs.avail_in == 0) |
| zs.avail_in = CHUNKSIZE; |
| Bytef *oldout = zs.next_out; |
| int ret = deflate(&zs, done ? Z_FINISH : Z_NO_FLUSH); |
| outbytes += zs.next_out - oldout; |
| if (ret == Z_MEM_ERROR) { |
| zs.avail_out = 0; |
| return OOM; |
| } |
| if (ret == Z_BUF_ERROR || (done && ret == Z_OK)) { |
| JS_ASSERT(zs.avail_out == 0); |
| return MOREOUTPUT; |
| } |
| JS_ASSERT_IF(!done, ret == Z_OK); |
| JS_ASSERT_IF(done, ret == Z_STREAM_END); |
| return done ? DONE : CONTINUE; |
| } |
| |
| bool |
| js::DecompressString(const unsigned char *inp, size_t inplen, unsigned char *out, size_t outlen) |
| { |
| JS_ASSERT(inplen <= UINT32_MAX); |
| z_stream zs; |
| zs.zalloc = zlib_alloc; |
| zs.zfree = zlib_free; |
| zs.opaque = NULL; |
| zs.next_in = (Bytef *)inp; |
| zs.avail_in = inplen; |
| zs.next_out = out; |
| JS_ASSERT(outlen); |
| zs.avail_out = outlen; |
| int ret = inflateInit(&zs); |
| if (ret != Z_OK) { |
| JS_ASSERT(ret == Z_MEM_ERROR); |
| return false; |
| } |
| ret = inflate(&zs, Z_FINISH); |
| JS_ASSERT(ret == Z_STREAM_END); |
| ret = inflateEnd(&zs); |
| JS_ASSERT(ret == Z_OK); |
| return true; |
| } |
| #endif |
| |
| #ifdef DEBUG |
| /* For JS_OOM_POSSIBLY_FAIL in jsutil.h. */ |
| JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations = UINT32_MAX; |
| JS_PUBLIC_DATA(uint32_t) OOM_counter = 0; |
| #endif |
| |
| /* |
| * Checks the assumption that JS_FUNC_TO_DATA_PTR and JS_DATA_TO_FUNC_PTR |
| * macros uses to implement casts between function and data pointers. |
| */ |
| JS_STATIC_ASSERT(sizeof(void *) == sizeof(void (*)())); |
| |
| JS_PUBLIC_API(void) |
| JS_Assert(const char *s, const char *file, int ln) |
| { |
| MOZ_ReportAssertionFailure(s, file, ln); |
| MOZ_CRASH(); |
| } |
| |
| #ifdef JS_BASIC_STATS |
| |
| #include <math.h> |
| |
| /* |
| * Histogram bins count occurrences of values <= the bin label, as follows: |
| * |
| * linear: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 or more |
| * 2**x: 0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512 or more |
| * 10**x: 0, 1, 10, 100, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9 or more |
| * |
| * We wish to count occurrences of 0 and 1 values separately, always. |
| */ |
| static uint32_t |
| BinToVal(unsigned logscale, unsigned bin) |
| { |
| JS_ASSERT(bin <= 10); |
| if (bin <= 1 || logscale == 0) |
| return bin; |
| --bin; |
| if (logscale == 2) |
| return JS_BIT(bin); |
| JS_ASSERT(logscale == 10); |
| return uint32_t(pow(10.0, (double) bin)); |
| } |
| |
| static unsigned |
| ValToBin(unsigned logscale, uint32_t val) |
| { |
| unsigned bin; |
| |
| if (val <= 1) |
| return val; |
| bin = (logscale == 10) |
| ? (unsigned) ceil(log10((double) val)) |
| : (logscale == 2) |
| ? (unsigned) JS_CEILING_LOG2W(val) |
| : val; |
| return Min(bin, 10U); |
| } |
| |
| void |
| JS_BasicStatsAccum(JSBasicStats *bs, uint32_t val) |
| { |
| unsigned oldscale, newscale, bin; |
| double mean; |
| |
| ++bs->num; |
| if (bs->max < val) |
| bs->max = val; |
| bs->sum += val; |
| bs->sqsum += (double)val * val; |
| |
| oldscale = bs->logscale; |
| if (oldscale != 10) { |
| mean = bs->sum / bs->num; |
| if (bs->max > 16 && mean > 8) { |
| newscale = (bs->max > 1e6 && mean > 1000) ? 10 : 2; |
| if (newscale != oldscale) { |
| uint32_t newhist[11], newbin; |
| |
| PodArrayZero(newhist); |
| for (bin = 0; bin <= 10; bin++) { |
| newbin = ValToBin(newscale, BinToVal(oldscale, bin)); |
| newhist[newbin] += bs->hist[bin]; |
| } |
| js_memcpy(bs->hist, newhist, sizeof bs->hist); |
| bs->logscale = newscale; |
| } |
| } |
| } |
| |
| bin = ValToBin(bs->logscale, val); |
| ++bs->hist[bin]; |
| } |
| |
| double |
| JS_MeanAndStdDev(uint32_t num, double sum, double sqsum, double *sigma) |
| { |
| double var; |
| |
| if (num == 0 || sum == 0) { |
| *sigma = 0; |
| return 0; |
| } |
| |
| var = num * sqsum - sum * sum; |
| if (var < 0 || num == 1) |
| var = 0; |
| else |
| var /= (double)num * (num - 1); |
| |
| /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */ |
| *sigma = (var != 0) ? sqrt(var) : 0; |
| return sum / num; |
| } |
| |
| void |
| JS_DumpBasicStats(JSBasicStats *bs, const char *title, FILE *fp) |
| { |
| double mean, sigma; |
| |
| mean = JS_MeanAndStdDevBS(bs, &sigma); |
| fprintf(fp, "\nmean %s %g, std. deviation %g, max %lu\n", |
| title, mean, sigma, (unsigned long) bs->max); |
| JS_DumpHistogram(bs, fp); |
| } |
| |
| void |
| JS_DumpHistogram(JSBasicStats *bs, FILE *fp) |
| { |
| unsigned bin; |
| uint32_t cnt, max; |
| double sum, mean; |
| |
| for (bin = 0, max = 0, sum = 0; bin <= 10; bin++) { |
| cnt = bs->hist[bin]; |
| if (max < cnt) |
| max = cnt; |
| sum += cnt; |
| } |
| mean = sum / cnt; |
| for (bin = 0; bin <= 10; bin++) { |
| unsigned val = BinToVal(bs->logscale, bin); |
| unsigned end = (bin == 10) ? 0 : BinToVal(bs->logscale, bin + 1); |
| cnt = bs->hist[bin]; |
| if (val + 1 == end) |
| fprintf(fp, " [%6u]", val); |
| else if (end != 0) |
| fprintf(fp, "[%6u, %6u]", val, end - 1); |
| else |
| fprintf(fp, "[%6u, +inf]", val); |
| fprintf(fp, ": %8u ", cnt); |
| if (cnt != 0) { |
| if (max > 1e6 && mean > 1e3) |
| cnt = uint32_t(ceil(log10((double) cnt))); |
| else if (max > 16 && mean > 8) |
| cnt = JS_CEILING_LOG2W(cnt); |
| for (unsigned i = 0; i < cnt; i++) |
| putc('*', fp); |
| } |
| putc('\n', fp); |
| } |
| } |
| |
| #endif /* JS_BASIC_STATS */ |