Christian Decker
4 years ago
1044 changed files with 253410 additions and 0 deletions
@ -0,0 +1,23 @@ |
|||||
|
Copyright (c) 2011 Aliaksandr Valialkin <valyala@gmail.com> |
||||
|
All rights reserved. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions |
||||
|
are met: |
||||
|
1. Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
2. Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in the |
||||
|
documentation and/or other materials provided with the distribution. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
||||
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
|
ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
||||
|
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
||||
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
||||
|
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
||||
|
SUCH DAMAGE. |
@ -0,0 +1,51 @@ |
|||||
|
CPP_COMPILER=g++ |
||||
|
C_COMPILER=gcc |
||||
|
|
||||
|
COMMON_CFLAGS=-Wall -Wextra -Werror -pedantic |
||||
|
|
||||
|
DEBUG_CFLAGS=-g |
||||
|
OPT_CFLAGS=-DNDEBUG -O2 |
||||
|
|
||||
|
C_CFLAGS=$(COMMON_CFLAGS) -std=c99 |
||||
|
CPP03_CFLAGS=$(COMMON_CFLAGS) -std=c++98 |
||||
|
CPP11_CFLAGS=$(COMMON_CFLAGS) -std=c++0x -DGHEAP_CPP11 |
||||
|
|
||||
|
all: tests perftests ops_count_test |
||||
|
|
||||
|
build-tests: |
||||
|
$(C_COMPILER) tests.c $(C_CFLAGS) $(DEBUG_CFLAGS) -o tests_c |
||||
|
$(CPP_COMPILER) tests.cpp $(CPP03_CFLAGS) $(DEBUG_CFLAGS) -o tests_cpp03 |
||||
|
$(CPP_COMPILER) tests.cpp $(CPP11_CFLAGS) $(DEBUG_CFLAGS) -o tests_cpp11 |
||||
|
|
||||
|
tests: build-tests |
||||
|
./tests_c |
||||
|
./tests_cpp03 |
||||
|
./tests_cpp11 |
||||
|
|
||||
|
build-perftests: |
||||
|
$(C_COMPILER) perftests.c $(C_CFLAGS) $(OPT_CFLAGS) -o perftests_c |
||||
|
$(CPP_COMPILER) perftests.cpp $(CPP03_CFLAGS) $(OPT_CFLAGS) -o perftests_cpp03 |
||||
|
$(CPP_COMPILER) perftests.cpp $(CPP11_CFLAGS) $(OPT_CFLAGS) -o perftests_cpp11 |
||||
|
|
||||
|
perftests: |
||||
|
./perftests_c |
||||
|
./perftests_cpp03 |
||||
|
./perftests_cpp11 |
||||
|
|
||||
|
build-ops_count_test: |
||||
|
$(CPP_COMPILER) ops_count_test.cpp $(CPP03_CFLAGS) $(OPT_CFLAGS) -o ops_count_test_cpp03 |
||||
|
$(CPP_COMPILER) ops_count_test.cpp $(CPP11_CFLAGS) $(OPT_CFLAGS) -o ops_count_test_cpp11 |
||||
|
|
||||
|
ops_count_test: |
||||
|
./ops_count_test_cpp03 |
||||
|
./ops_count_test_cpp11 |
||||
|
|
||||
|
clean: |
||||
|
rm -f ./tests_c |
||||
|
rm -f ./tests_cpp03 |
||||
|
rm -f ./tests_cpp11 |
||||
|
rm -f ./perftests_c |
||||
|
rm -f ./perftests_cpp03 |
||||
|
rm -f ./perftests_cpp11 |
||||
|
rm -f ./ops_count_test_cpp03 |
||||
|
rm -f ./ops_count_test_cpp11 |
@ -0,0 +1,176 @@ |
|||||
|
Generalized heap implementation |
||||
|
|
||||
|
Generalized heap is based on usual heap data structure - |
||||
|
http://en.wikipedia.org/wiki/Heap_%28data_structure%29 . |
||||
|
|
||||
|
It provides two additional paremeters, which allow optimizing heap |
||||
|
for particular cases: |
||||
|
|
||||
|
* Fanout. The number of children per each heap node. |
||||
|
* Fanout=1 corresponds to sorted List data structure. |
||||
|
See http://en.wikipedia.org/wiki/List_%28computing%29 . |
||||
|
* Fanout=2 corresponds to Binary heap. |
||||
|
See http://en.wikipedia.org/wiki/Binary_heap . |
||||
|
* Fanout>2 corresponds to D-heap. See http://en.wikipedia.org/wiki/D-heap . |
||||
|
D-heap can be faster than Binary heap in the following cases: |
||||
|
* If item comparison is faster than item assignment. |
||||
|
* If sequential access to items is faster than non-sequential access |
||||
|
to items. |
||||
|
* If the number of 'decrease key' operations is larger than the number |
||||
|
of 'pop heap' operations for min-heap. This is usually the case |
||||
|
for Dijkstra algorithm |
||||
|
( http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm ). |
||||
|
|
||||
|
* PageChunks. The number of chunks per each heap page. Each chunk contains |
||||
|
Fanout items, so each heap page contains (PageChunks * Fanout) items. |
||||
|
Items inside heap page are organized into a sub-heap with a root item outside |
||||
|
the page. Leaf items in the page can be roots pointing to another pages. |
||||
|
* PageChunks=1 corresponds to standard heap. |
||||
|
* PageChunks>1 corresponds to B-heap. See http://en.wikipedia.org/wiki/B-heap. |
||||
|
Heap pages containing more than one page chunk can be useful if multiple |
||||
|
item accesses inside heap page is faster than multiple accesses to items |
||||
|
across distinct heap pages. This can be the case for systems with virtual |
||||
|
memory, where VM pages can be swapped out to slow media. |
||||
|
Heap pages can be mapped to VM pages if PageChunks is calculated using |
||||
|
the following formula: |
||||
|
* PageChunks = sizeof(VM_page) / (sizeof(item) * Fanout) |
||||
|
Perfrect alginment between VM pages and heap pages can be achieved if |
||||
|
heap's root item is placed at the end of VM page. In this case the first |
||||
|
child of the heap's root (i.e. the item with index 1) sits at the beginning |
||||
|
of the next VM page. |
||||
|
|
||||
|
See also https://github.com/valyala/gheap/tree/sophisticated-gheap branch, |
||||
|
which contains sophisticated gheap implementation with more complex heap layout |
||||
|
and low-level optimizations. |
||||
|
|
||||
|
|
||||
|
=============================================================================== |
||||
|
The implementation provides the following functions: |
||||
|
* Auxiliary functions: |
||||
|
* get_parent_index() - returns parent index for the given child. |
||||
|
* get_child_index() - returns the first child index for the given parent. |
||||
|
* swap_max_item() - swaps heap's maximum item with the item outside heap |
||||
|
and restores max heap invariant. |
||||
|
* restore_heap_after_item_increase() - restores max heap invariant after |
||||
|
the item's value increase. |
||||
|
* restore_heap_after_item_decrease() - restores max heap invariant after |
||||
|
the item's value decrease. |
||||
|
* remove_from_heap() - removes the given item from the heap. |
||||
|
|
||||
|
* STL-like functions: |
||||
|
* is_heap_until() - returns an iterator to the first non-heap item |
||||
|
in the given range. |
||||
|
* is_heap() - checks whether the given range contains valid heap. |
||||
|
* make_heap() - creates a heap. |
||||
|
* push_heap() - pushes the last element in the range to the heap. |
||||
|
* pop_heap() - pops up the maximum element from the heap. |
||||
|
* sort_heap() - sorts heap items in ascending order. |
||||
|
|
||||
|
* Heap-based algorithms: |
||||
|
* heapsort() - performs heapsort. |
||||
|
* partial_sort() - performs partial sort. |
||||
|
* nway_merge() - performs N-way merge on top of the heap. |
||||
|
* nway_mergesort() - performs N-way mergesort on top of the heap. |
||||
|
|
||||
|
The implementation is inspired by http://queue.acm.org/detail.cfm?id=1814327 , |
||||
|
but it is more generalized. The implementation is optimized for speed. |
||||
|
There are the following files: |
||||
|
* gheap_cpp03.hpp - gheap optimized for C++03. |
||||
|
* gheap_cpp11.hpp - gheap optimized for C++11. |
||||
|
* gheap.hpp - switch file, which includes either gheap_cpp03.hpp |
||||
|
or gheap_cpp11.hpp depending on whether GHEAP_CPP11 macro is defined. |
||||
|
* gheap.h - gheap optimized for C99. |
||||
|
* galgorithm.hpp - various algorithms on top of gheap for C++. |
||||
|
* galgorithm.h - various algorithms on top of gheap for C99. |
||||
|
* gpriority_queue.hpp - priority queue on top of gheap for C++. |
||||
|
* gpriority_queue.h - priority queue on top of gheap for C99. |
||||
|
|
||||
|
Don't forget passing -DNDEBUG option to the compiler when creating optimized |
||||
|
builds. This significantly speeds up gheap code by removing debug assertions. |
||||
|
|
||||
|
There are the following tests: |
||||
|
* tests.cpp and tests.c - tests for gheap algorithms' correctness. |
||||
|
* perftests.cpp and perftests.c - performance tests. |
||||
|
* ops_count_test.cpp - the test, which counts the number of varius operations |
||||
|
performed by gheap algorithms. |
||||
|
|
||||
|
=============================================================================== |
||||
|
gheap for C++ usage |
||||
|
|
||||
|
#include "gheap.hpp" |
||||
|
|
||||
|
... |
||||
|
|
||||
|
template <class Heap> |
||||
|
void heapsort(vector<int> &a) |
||||
|
{ |
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
Heap::sort_heap(a.begin(), a.end()); |
||||
|
} |
||||
|
|
||||
|
typedef gheap<2, 1> binary_heap; |
||||
|
heapsort<binary_heap>(a); |
||||
|
|
||||
|
typedef gheap<4, 1> d4_heap; |
||||
|
heapsort<d4_heap>(a); |
||||
|
|
||||
|
typedef gheap<2, 512> paged_binary_heap; |
||||
|
heapsort<paged_binary_heap>(a); |
||||
|
|
||||
|
|
||||
|
=============================================================================== |
||||
|
gheap for C usage |
||||
|
|
||||
|
#include "gheap.h" |
||||
|
|
||||
|
static void less(const void *const ctx, const void *const a, |
||||
|
const void *const b) |
||||
|
{ |
||||
|
(void)ctx; |
||||
|
return *(int *)a < *(int *)b; |
||||
|
} |
||||
|
|
||||
|
static void move(void *const dst, const void *const src) |
||||
|
{ |
||||
|
*(int *)dst = *(int *)src; |
||||
|
} |
||||
|
|
||||
|
static void heapsort(const struct gheap_ctx *const ctx, |
||||
|
int *const a, const size_t n) |
||||
|
{ |
||||
|
gheap_make_heap(ctx, a, n); |
||||
|
gheap_sort_heap(ctx, a, n); |
||||
|
} |
||||
|
|
||||
|
/* heapsort using binary heap */ |
||||
|
static const struct gheap_ctx binary_heap_ctx = { |
||||
|
.fanout = 2, |
||||
|
.page_chunks = 1, |
||||
|
.item_size = sizeof(int), |
||||
|
.less_comparer = &less, |
||||
|
.less_comparer_ctx = NULL, |
||||
|
.item_mover = &move, |
||||
|
}; |
||||
|
heapsort(&binary_heap_ctx, a, n); |
||||
|
|
||||
|
/* heapsort using D-4 heap */ |
||||
|
static const struct gheap_ctx d4_heap_ctx = { |
||||
|
.fanout = 4, |
||||
|
.page_chunks = 1, |
||||
|
.item_size = sizeof(int), |
||||
|
.less_comparer = &less, |
||||
|
.less_comparer_ctx = NULL, |
||||
|
.item_mover = &move, |
||||
|
}; |
||||
|
heapsort(&d4_heap_ctx, a, n); |
||||
|
|
||||
|
/* heapsort using paged binary heap */ |
||||
|
static const struct gheap_ctx paged_binary_heap_ctx = { |
||||
|
.fanout = 2, |
||||
|
.page_chunks = 512, |
||||
|
.item_size = sizeof(int), |
||||
|
.less_comparer = &less, |
||||
|
.less_comparer_ctx = NULL, |
||||
|
.item_mover = &move, |
||||
|
}; |
||||
|
heapsort(&paged_binary_heap_ctx, a, n); |
@ -0,0 +1,518 @@ |
|||||
|
#ifndef GALGORITHM_H |
||||
|
#define GALGORITHM_H |
||||
|
|
||||
|
/*
|
||||
|
* Generalized aglogithms based on gheap for C99. |
||||
|
* |
||||
|
* Don't forget passing -DNDEBUG option to the compiler when creating optimized |
||||
|
* builds. This significantly speeds up gheap code by removing debug assertions. |
||||
|
* |
||||
|
* Author: Aliaksandr Valialkin <valyala@gmail.com>. |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
/*******************************************************************************
|
||||
|
* Interface. |
||||
|
******************************************************************************/ |
||||
|
|
||||
|
#include "gheap.h" /* for gheap_ctx */ |
||||
|
|
||||
|
#include <stddef.h> /* for size_t */ |
||||
|
|
||||
|
/*
|
||||
|
* Sorts [base[0] ... base[n-1]] in ascending order via heapsort. |
||||
|
*/ |
||||
|
static inline void galgorithm_heapsort(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t n); |
||||
|
|
||||
|
/*
|
||||
|
* Performs partial sort, so [base[0] ... base[middle_index-1]) will contain |
||||
|
* items sorted in ascending order, which are smaller than the rest of items |
||||
|
* in the [base[middle_index] ... base[n-1]). |
||||
|
*/ |
||||
|
static inline void galgorithm_partial_sort(const struct gheap_ctx *ctx, |
||||
|
void *base, size_t n, size_t middle_index); |
||||
|
|
||||
|
/*
|
||||
|
* Vtable for input iterators, which is passed to galgorithm_nway_merge(). |
||||
|
*/ |
||||
|
struct galgorithm_nway_merge_input_vtable |
||||
|
{ |
||||
|
/*
|
||||
|
* Must advance the iterator to the next item. |
||||
|
* Must return non-zero on success or 0 on the end of input. |
||||
|
* |
||||
|
* Galgorithm won't call this function after it returns 0. |
||||
|
*/ |
||||
|
int (*next)(void *ctx); |
||||
|
|
||||
|
/*
|
||||
|
* Must return a pointer to the current item. |
||||
|
* |
||||
|
* Galgorithm won't call this function after the next() returns 0. |
||||
|
*/ |
||||
|
const void *(*get)(const void *ctx); |
||||
|
}; |
||||
|
|
||||
|
/*
|
||||
|
* A collection of input iterators, which is passed to galgorithm_nway_merge(). |
||||
|
*/ |
||||
|
struct galgorithm_nway_merge_input |
||||
|
{ |
||||
|
const struct galgorithm_nway_merge_input_vtable *vtable; |
||||
|
|
||||
|
/*
|
||||
|
* An array of opaque contexts, which are passed to vtable functions. |
||||
|
* Each context represents a single input iterator. |
||||
|
* Contextes must contain data reqired for fetching items from distinct |
||||
|
* input iterators. |
||||
|
* |
||||
|
* Contextes in this array can be shuffled using ctx_mover. |
||||
|
*/ |
||||
|
void *ctxs; |
||||
|
|
||||
|
/* The number of contextes. */ |
||||
|
size_t ctxs_count; |
||||
|
|
||||
|
/* The size of each context object. */ |
||||
|
size_t ctx_size; |
||||
|
|
||||
|
/* Is used for shuffling context objects. */ |
||||
|
gheap_item_mover_t ctx_mover; |
||||
|
}; |
||||
|
|
||||
|
/*
|
||||
|
* Vtable for output iterator, which is passed to galgorithm_nway_merge(). |
||||
|
*/ |
||||
|
struct galgorithm_nway_merge_output_vtable |
||||
|
{ |
||||
|
/*
|
||||
|
* Must put data into the output and advance the iterator |
||||
|
* to the next position. |
||||
|
*/ |
||||
|
void (*put)(void *ctx, const void *data); |
||||
|
}; |
||||
|
|
||||
|
/*
|
||||
|
* Output iterator, which is passed to galgorithm_nway_merge(). |
||||
|
*/ |
||||
|
struct galgorithm_nway_merge_output |
||||
|
{ |
||||
|
const struct galgorithm_nway_merge_output_vtable *vtable; |
||||
|
|
||||
|
/*
|
||||
|
* An opaque context, which is passed to vtable functions. |
||||
|
* The context must contain data essential for the output iterator. |
||||
|
*/ |
||||
|
void *ctx; |
||||
|
}; |
||||
|
|
||||
|
/*
|
||||
|
* Performs N-way merging of the given inputs into the output sorted |
||||
|
* in ascending order, using ctx->less_comparer for items' comparison. |
||||
|
* |
||||
|
* Each input must hold non-zero number of items sorted in ascending order. |
||||
|
* |
||||
|
* As a side effect the function shuffles input contextes. |
||||
|
*/ |
||||
|
static inline void galgorithm_nway_merge(const struct gheap_ctx *ctx, |
||||
|
const struct galgorithm_nway_merge_input *input, |
||||
|
const struct galgorithm_nway_merge_output *output); |
||||
|
|
||||
|
/*
|
||||
|
* Must sort the range [base[0] ... base[n-1]]. |
||||
|
* ctx is small_range_sorter_ctx passed to galgorithm_nway_mergesort. |
||||
|
*/ |
||||
|
typedef void (*galgorithm_nway_mergesort_small_range_sorter_t)( |
||||
|
const void *ctx, void *base, size_t n); |
||||
|
|
||||
|
/*
|
||||
|
* Performs n-way mergesort for [base[0] ... base[range_size-1]] items. |
||||
|
* |
||||
|
* Uses small_range_sorter for sorting ranges containing no more |
||||
|
* than small_range_size items. |
||||
|
* |
||||
|
* Splits the input range into subranges with small_range_size size, |
||||
|
* sorts them using small_range_sorter and then merges them back |
||||
|
* using n-way merge with n = subranges_count. |
||||
|
* |
||||
|
* items_tmp_buf must point to an uninitialized memory, which can hold |
||||
|
* up to range_size items. |
||||
|
*/ |
||||
|
static inline void galgorithm_nway_mergesort(const struct gheap_ctx *ctx, |
||||
|
void *base, size_t range_size, |
||||
|
galgorithm_nway_mergesort_small_range_sorter_t small_range_sorter, |
||||
|
const void *small_range_sorter_ctx, |
||||
|
size_t small_range_size, size_t subranges_count, void *items_tmp_buf); |
||||
|
|
||||
|
|
||||
|
/*******************************************************************************
|
||||
|
* Implementation. |
||||
|
* |
||||
|
* Define all functions inline, so compiler will be able optimizing out common |
||||
|
* args (fanout, page_chunks, item_size, less_comparer and item_mover), |
||||
|
* which are usually constants, using constant folding optimization |
||||
|
* ( http://en.wikipedia.org/wiki/Constant_folding ).
|
||||
|
*****************************************************************************/ |
||||
|
|
||||
|
#include "gheap.h" /* for gheap_* stuff */ |
||||
|
|
||||
|
#include <assert.h> /* for assert */ |
||||
|
#include <stddef.h> /* for size_t */ |
||||
|
#include <stdint.h> /* for uintptr_t, SIZE_MAX and UINTPTR_MAX */ |
||||
|
#include <stdlib.h> /* for malloc(), free() */ |
||||
|
|
||||
|
/* Returns a pointer to base[index]. */ |
||||
|
static inline void *_galgorithm_get_item_ptr( |
||||
|
const struct gheap_ctx *const ctx, |
||||
|
const void *const base, const size_t index) |
||||
|
{ |
||||
|
const size_t item_size = ctx->item_size; |
||||
|
|
||||
|
assert(index <= SIZE_MAX / item_size); |
||||
|
|
||||
|
const size_t offset = item_size * index; |
||||
|
assert((uintptr_t)base <= UINTPTR_MAX - offset); |
||||
|
|
||||
|
return ((char *)base) + offset; |
||||
|
} |
||||
|
|
||||
|
/* Swaps items with given indexes */ |
||||
|
static inline void _galgorithm_swap_items(const struct gheap_ctx *const ctx, |
||||
|
const void *const base, const size_t a_index, const size_t b_index) |
||||
|
{ |
||||
|
const size_t item_size = ctx->item_size; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
char tmp[item_size]; |
||||
|
void *const a = _galgorithm_get_item_ptr(ctx, base, a_index); |
||||
|
void *const b = _galgorithm_get_item_ptr(ctx, base, b_index); |
||||
|
item_mover(tmp, a); |
||||
|
item_mover(a, b); |
||||
|
item_mover(b, tmp); |
||||
|
} |
||||
|
|
||||
|
static inline void galgorithm_heapsort(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t n) |
||||
|
{ |
||||
|
gheap_make_heap(ctx, base, n); |
||||
|
gheap_sort_heap(ctx, base, n); |
||||
|
} |
||||
|
|
||||
|
static inline void galgorithm_partial_sort(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t n, const size_t middle_index) |
||||
|
{ |
||||
|
assert(middle_index <= n); |
||||
|
|
||||
|
if (middle_index > 0) { |
||||
|
gheap_make_heap(ctx, base, middle_index); |
||||
|
|
||||
|
const gheap_less_comparer_t less_comparer = ctx->less_comparer; |
||||
|
const void *const less_comparer_ctx = ctx->less_comparer_ctx; |
||||
|
|
||||
|
for (size_t i = middle_index; i < n; ++i) { |
||||
|
void *const tmp = _galgorithm_get_item_ptr(ctx, base, i); |
||||
|
if (less_comparer(less_comparer_ctx, tmp, base)) { |
||||
|
gheap_swap_max_item(ctx, base, middle_index, tmp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
gheap_sort_heap(ctx, base, middle_index); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct _galgorithm_nway_merge_less_comparer_ctx |
||||
|
{ |
||||
|
gheap_less_comparer_t less_comparer; |
||||
|
const void *less_comparer_ctx; |
||||
|
const struct galgorithm_nway_merge_input_vtable *vtable; |
||||
|
}; |
||||
|
|
||||
|
static inline int _galgorithm_nway_merge_less_comparer(const void *const ctx, |
||||
|
const void *const a, const void *const b) |
||||
|
{ |
||||
|
const struct _galgorithm_nway_merge_less_comparer_ctx *const c = ctx; |
||||
|
const gheap_less_comparer_t less_comparer = c->less_comparer; |
||||
|
const void *const less_comparer_ctx = c->less_comparer_ctx; |
||||
|
const struct galgorithm_nway_merge_input_vtable *const vtable = c->vtable; |
||||
|
|
||||
|
return less_comparer(less_comparer_ctx, vtable->get(b), vtable->get(a)); |
||||
|
} |
||||
|
|
||||
|
static inline void galgorithm_nway_merge(const struct gheap_ctx *const ctx, |
||||
|
const struct galgorithm_nway_merge_input *const input, |
||||
|
const struct galgorithm_nway_merge_output *const output) |
||||
|
{ |
||||
|
void *const top_input = input->ctxs; |
||||
|
size_t inputs_count = input->ctxs_count; |
||||
|
|
||||
|
assert(inputs_count > 0); |
||||
|
|
||||
|
const struct _galgorithm_nway_merge_less_comparer_ctx less_comparer_ctx = { |
||||
|
.less_comparer = ctx->less_comparer, |
||||
|
.less_comparer_ctx = ctx->less_comparer_ctx, |
||||
|
.vtable = input->vtable, |
||||
|
}; |
||||
|
const struct gheap_ctx nway_ctx = { |
||||
|
.fanout = ctx->fanout, |
||||
|
.page_chunks = ctx->page_chunks, |
||||
|
.item_size = input->ctx_size, |
||||
|
.less_comparer = &_galgorithm_nway_merge_less_comparer, |
||||
|
.less_comparer_ctx = &less_comparer_ctx, |
||||
|
.item_mover = input->ctx_mover, |
||||
|
}; |
||||
|
|
||||
|
gheap_make_heap(&nway_ctx, top_input, inputs_count); |
||||
|
while (1) { |
||||
|
const void *const data = input->vtable->get(top_input); |
||||
|
output->vtable->put(output->ctx, data); |
||||
|
if (!input->vtable->next(top_input)) { |
||||
|
--inputs_count; |
||||
|
if (inputs_count == 0) { |
||||
|
break; |
||||
|
} |
||||
|
_galgorithm_swap_items(&nway_ctx, top_input, 0, inputs_count); |
||||
|
} |
||||
|
gheap_restore_heap_after_item_decrease(&nway_ctx, top_input, |
||||
|
inputs_count, 0); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static inline void _galgorithm_move_items(const struct gheap_ctx *const ctx, |
||||
|
void *const src, const size_t n, void *const dst) |
||||
|
{ |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
item_mover( |
||||
|
_galgorithm_get_item_ptr(ctx, dst, i), |
||||
|
_galgorithm_get_item_ptr(ctx, src, i)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static inline void _galgorithm_sort_subranges(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t range_size, |
||||
|
const galgorithm_nway_mergesort_small_range_sorter_t small_range_sorter, |
||||
|
const void *const small_range_sorter_ctx, |
||||
|
const size_t small_range_size) |
||||
|
{ |
||||
|
assert(small_range_size > 0); |
||||
|
|
||||
|
const size_t last_full_range = range_size - range_size % small_range_size; |
||||
|
for (size_t i = 0; i != last_full_range; i += small_range_size) { |
||||
|
small_range_sorter(small_range_sorter_ctx, |
||||
|
_galgorithm_get_item_ptr(ctx, base, i), small_range_size); |
||||
|
} |
||||
|
|
||||
|
/* Sort the last subrange, which contains less than small_range_size items. */ |
||||
|
if (last_full_range < range_size) { |
||||
|
small_range_sorter(small_range_sorter_ctx, |
||||
|
_galgorithm_get_item_ptr(ctx, base, last_full_range), |
||||
|
range_size - last_full_range); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
struct _galgorithm_nway_mergesort_input_ctx |
||||
|
{ |
||||
|
const struct gheap_ctx *ctx; |
||||
|
const void *next; |
||||
|
const void *last; |
||||
|
}; |
||||
|
|
||||
|
static inline int _galgorithm_nway_mergesort_input_next(void *ctx) |
||||
|
{ |
||||
|
struct _galgorithm_nway_mergesort_input_ctx *const c = ctx; |
||||
|
|
||||
|
assert(c->next < c->last); |
||||
|
c->next = _galgorithm_get_item_ptr(c->ctx, c->next, 1); |
||||
|
assert(c->next <= c->last); |
||||
|
return (c->next < c->last); |
||||
|
} |
||||
|
|
||||
|
static inline const void *_galgorithm_nway_mergesort_input_get(const void *ctx) |
||||
|
{ |
||||
|
const struct _galgorithm_nway_mergesort_input_ctx *const c = ctx; |
||||
|
|
||||
|
assert(c->next < c->last); |
||||
|
return c->next; |
||||
|
} |
||||
|
|
||||
|
static const struct galgorithm_nway_merge_input_vtable |
||||
|
_galgorithm_nway_mergesort_input_vtable = { |
||||
|
.next = &_galgorithm_nway_mergesort_input_next, |
||||
|
.get = &_galgorithm_nway_mergesort_input_get, |
||||
|
}; |
||||
|
|
||||
|
struct _galgorithm_nway_mergesort_output_ctx |
||||
|
{ |
||||
|
const struct gheap_ctx *ctx; |
||||
|
void *next; |
||||
|
}; |
||||
|
|
||||
|
static inline void _galgorithm_nway_mergesort_output_put(void *ctx, |
||||
|
const void *data) |
||||
|
{ |
||||
|
struct _galgorithm_nway_mergesort_output_ctx *const c = ctx; |
||||
|
const gheap_item_mover_t item_mover = c->ctx->item_mover; |
||||
|
|
||||
|
item_mover(c->next, data); |
||||
|
c->next = _galgorithm_get_item_ptr(c->ctx, c->next, 1); |
||||
|
} |
||||
|
|
||||
|
static const struct galgorithm_nway_merge_output_vtable |
||||
|
_galgorithm_nway_mergesort_output_vtable = { |
||||
|
.put = &_galgorithm_nway_mergesort_output_put, |
||||
|
}; |
||||
|
|
||||
|
static inline void _galgorithm_merge_subrange_tuples( |
||||
|
const struct gheap_ctx *const ctx, void *const base, const size_t range_size, |
||||
|
struct galgorithm_nway_merge_input *const input, |
||||
|
const struct galgorithm_nway_merge_output *const output, |
||||
|
const size_t subranges_count, const size_t subrange_size) |
||||
|
{ |
||||
|
assert(subranges_count > 1); |
||||
|
assert(subrange_size > 0); |
||||
|
|
||||
|
struct _galgorithm_nway_mergesort_input_ctx *const input_ctxs = input->ctxs; |
||||
|
input->ctxs_count = subranges_count; |
||||
|
|
||||
|
size_t i = 0; |
||||
|
|
||||
|
/*
|
||||
|
* Merge full subrange tuples. Each full subrange tuple contains |
||||
|
* subranges_count full subranges. Each full subrange contains |
||||
|
* subrange_size items. |
||||
|
*/ |
||||
|
if (subrange_size <= range_size / subranges_count) { |
||||
|
const size_t tuple_size = subrange_size * subranges_count; |
||||
|
const size_t last_full_tuple = range_size - range_size % tuple_size; |
||||
|
|
||||
|
while (i != last_full_tuple) { |
||||
|
for (size_t j = 0; j < subranges_count; ++j) { |
||||
|
input_ctxs[j].next = _galgorithm_get_item_ptr(ctx, base, i); |
||||
|
i += subrange_size; |
||||
|
input_ctxs[j].last = _galgorithm_get_item_ptr(ctx, base, i); |
||||
|
} |
||||
|
|
||||
|
galgorithm_nway_merge(ctx, input, output); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/*
|
||||
|
* Merge tail subrange tuple. Tail subrange tuple contains less than |
||||
|
* subranges_count full subranges. It also may contain tail subrange |
||||
|
* with less than subrange_size items. |
||||
|
*/ |
||||
|
const size_t tail_tuple_size = range_size - i; |
||||
|
if (tail_tuple_size > 0) { |
||||
|
const size_t full_subranges_count = tail_tuple_size / subrange_size; |
||||
|
assert(full_subranges_count < subranges_count); |
||||
|
size_t tail_subranges_count = full_subranges_count; |
||||
|
|
||||
|
for (size_t j = 0; j < full_subranges_count; ++j) { |
||||
|
input_ctxs[j].next = _galgorithm_get_item_ptr(ctx, base, i); |
||||
|
i += subrange_size; |
||||
|
input_ctxs[j].last = _galgorithm_get_item_ptr(ctx, base, i); |
||||
|
} |
||||
|
|
||||
|
if (i < range_size) { |
||||
|
input_ctxs[full_subranges_count].next = |
||||
|
_galgorithm_get_item_ptr(ctx, base, i); |
||||
|
input_ctxs[full_subranges_count].last = |
||||
|
_galgorithm_get_item_ptr(ctx, base, range_size); |
||||
|
++tail_subranges_count; |
||||
|
} |
||||
|
|
||||
|
input->ctxs_count = tail_subranges_count; |
||||
|
galgorithm_nway_merge(ctx, input, output); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static inline void _galgorithm_nway_mergesort_input_ctx_mover(void *dst, |
||||
|
const void *src) |
||||
|
{ |
||||
|
*(struct _galgorithm_nway_mergesort_input_ctx *)dst = |
||||
|
*(struct _galgorithm_nway_mergesort_input_ctx *)src; |
||||
|
} |
||||
|
|
||||
|
static inline void galgorithm_nway_mergesort(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t range_size, |
||||
|
const galgorithm_nway_mergesort_small_range_sorter_t small_range_sorter, |
||||
|
const void *const small_range_sorter_ctx, |
||||
|
const size_t small_range_size, const size_t subranges_count, |
||||
|
void *const items_tmp_buf) |
||||
|
{ |
||||
|
assert(small_range_size > 0); |
||||
|
assert(subranges_count > 1); |
||||
|
|
||||
|
/* Preparation: Move items to a temporary buffer. */ |
||||
|
_galgorithm_move_items(ctx, base, range_size, items_tmp_buf); |
||||
|
|
||||
|
/*
|
||||
|
* Step 1: split the range into subranges with small_range_size size each |
||||
|
* (except the last subrange, which may contain less than small_range_size |
||||
|
* items) and sort each of these subranges using small_range_sorter. |
||||
|
*/ |
||||
|
_galgorithm_sort_subranges(ctx, items_tmp_buf, range_size, |
||||
|
small_range_sorter, small_range_sorter_ctx, small_range_size); |
||||
|
|
||||
|
/* Step 2: Merge subranges sorted at the previous step using n-way merge. */ |
||||
|
struct _galgorithm_nway_mergesort_input_ctx *const input_ctxs = |
||||
|
malloc(sizeof(input_ctxs[0]) * subranges_count); |
||||
|
for (size_t i = 0; i < subranges_count; ++i) { |
||||
|
input_ctxs[i].ctx = ctx; |
||||
|
} |
||||
|
|
||||
|
struct galgorithm_nway_merge_input input = { |
||||
|
.vtable = &_galgorithm_nway_mergesort_input_vtable, |
||||
|
.ctxs = input_ctxs, |
||||
|
.ctxs_count = subranges_count, |
||||
|
.ctx_size = sizeof(input_ctxs[0]), |
||||
|
.ctx_mover = &_galgorithm_nway_mergesort_input_ctx_mover, |
||||
|
}; |
||||
|
|
||||
|
struct _galgorithm_nway_mergesort_output_ctx output_ctx; |
||||
|
output_ctx.ctx = ctx; |
||||
|
|
||||
|
const struct galgorithm_nway_merge_output output = { |
||||
|
.vtable = &_galgorithm_nway_mergesort_output_vtable, |
||||
|
.ctx = &output_ctx, |
||||
|
}; |
||||
|
|
||||
|
size_t subrange_size = small_range_size; |
||||
|
for (;;) { |
||||
|
/*
|
||||
|
* First pass: merge items from the temporary buffer |
||||
|
* to the original location. |
||||
|
*/ |
||||
|
output_ctx.next = base; |
||||
|
_galgorithm_merge_subrange_tuples(ctx, items_tmp_buf, range_size, |
||||
|
&input, &output, subranges_count, subrange_size); |
||||
|
|
||||
|
if (subrange_size > range_size / subranges_count) { |
||||
|
break; |
||||
|
} |
||||
|
subrange_size *= subranges_count; |
||||
|
|
||||
|
/*
|
||||
|
* Second pass: merge items from the original location |
||||
|
* to the temporary buffer. |
||||
|
*/ |
||||
|
output_ctx.next = items_tmp_buf; |
||||
|
_galgorithm_merge_subrange_tuples(ctx, base, range_size, |
||||
|
&input, &output, subranges_count, subrange_size); |
||||
|
|
||||
|
if (subrange_size > range_size / subranges_count) { |
||||
|
/* Move items from the temporary buffer to the original location. */ |
||||
|
_galgorithm_move_items(ctx, items_tmp_buf, range_size, base); |
||||
|
break; |
||||
|
} |
||||
|
subrange_size *= subranges_count; |
||||
|
} |
||||
|
|
||||
|
free(input_ctxs); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
#endif |
@ -0,0 +1,573 @@ |
|||||
|
#ifndef GALGORITHM_H |
||||
|
#define GALGORITHM_H |
||||
|
|
||||
|
// Generalized algorithms based on Heap.
|
||||
|
//
|
||||
|
// Pass -DGHEAP_CPP11 to compiler for enabling C++11 optimization,
|
||||
|
// otherwise C++03 optimization will be enabled.
|
||||
|
//
|
||||
|
// Don't forget passing -DNDEBUG option to the compiler when creating optimized
|
||||
|
// builds. This significantly speeds up the code by removing debug assertions.
|
||||
|
//
|
||||
|
// Author: Aliaksandr Valialkin <valyala@gmail.com>.
|
||||
|
|
||||
|
#include "gheap.hpp" |
||||
|
|
||||
|
#include <cassert> // for assert |
||||
|
#include <cstddef> // for size_t, ptrdiff_t |
||||
|
#include <iterator> // for std::iterator_traits, std::advance() |
||||
|
#include <memory> // for std::*_temporary_buffer() |
||||
|
#include <new> // for std::bad_alloc |
||||
|
#include <utility> // for std::move(), std::swap(), std::*pair |
||||
|
|
||||
|
template <class Heap = gheap<> > |
||||
|
class galgorithm |
||||
|
{ |
||||
|
private: |
||||
|
|
||||
|
// Standard less comparer.
|
||||
|
template <class InputIterator> |
||||
|
static bool _std_less_comparer( |
||||
|
const typename std::iterator_traits<InputIterator>::value_type &a, |
||||
|
const typename std::iterator_traits<InputIterator>::value_type &b) |
||||
|
{ |
||||
|
return (a < b); |
||||
|
} |
||||
|
|
||||
|
// Less comparer for nway_merge().
|
||||
|
template <class LessComparer> |
||||
|
class _nway_merge_less_comparer |
||||
|
{ |
||||
|
private: |
||||
|
const LessComparer &_less_comparer; |
||||
|
|
||||
|
public: |
||||
|
_nway_merge_less_comparer(const LessComparer &less_comparer) : |
||||
|
_less_comparer(less_comparer) {} |
||||
|
|
||||
|
template <class InputIterator> |
||||
|
bool operator() ( |
||||
|
const std::pair<InputIterator, InputIterator> &input_range_a, |
||||
|
const std::pair<InputIterator, InputIterator> &input_range_b) const |
||||
|
{ |
||||
|
assert(input_range_a.first != input_range_a.second); |
||||
|
assert(input_range_b.first != input_range_b.second); |
||||
|
|
||||
|
return _less_comparer(*(input_range_b.first), *(input_range_a.first)); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// RAII wrapper around temporary buffer.
|
||||
|
// It is used by nway_mergesort() for allocation of temporary memory.
|
||||
|
template <class T> |
||||
|
class _temporary_buffer |
||||
|
{ |
||||
|
private: |
||||
|
T *_ptr; |
||||
|
|
||||
|
public: |
||||
|
_temporary_buffer(const size_t size) |
||||
|
{ |
||||
|
const std::pair<T *, ptrdiff_t> tmp_buf = |
||||
|
std::get_temporary_buffer<T>(size); |
||||
|
_ptr = tmp_buf.first; |
||||
|
assert(tmp_buf.second >= 0); |
||||
|
if (_ptr == 0 || (size_t)tmp_buf.second < size) { |
||||
|
// It is OK passing (_ptr == 0) to std::return_temporary_buffer().
|
||||
|
std::return_temporary_buffer(_ptr); |
||||
|
throw std::bad_alloc(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
~_temporary_buffer() |
||||
|
{ |
||||
|
std::return_temporary_buffer(_ptr); |
||||
|
_ptr = 0; |
||||
|
} |
||||
|
|
||||
|
T *get_ptr() const |
||||
|
{ |
||||
|
return _ptr; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Standard sorter for small ranges.
|
||||
|
template <class T, class LessComparer> |
||||
|
static void _std_small_range_sorter(T *const first, T *const last, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(first <= last); |
||||
|
|
||||
|
// Insertion sort implementation.
|
||||
|
// See http://en.wikipedia.org/wiki/Insertion_sort .
|
||||
|
|
||||
|
for (T *it = first + 1; it != last; ++it) { |
||||
|
#ifdef GHEAP_CPP11 |
||||
|
T tmp = std::move(*it); |
||||
|
T *hole = it; |
||||
|
while (hole != first && less_comparer(tmp, *(hole - 1))) { |
||||
|
*hole = std::move(*(hole - 1)); |
||||
|
--hole; |
||||
|
} |
||||
|
*hole = std::move(tmp); |
||||
|
#else |
||||
|
T *hole = it; |
||||
|
while (hole != first && less_comparer(*hole, *(hole - 1))) { |
||||
|
std::swap(*hole, *(hole - 1)); |
||||
|
--hole; |
||||
|
} |
||||
|
#endif |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Moves items from [first ... last) to uninitialized memory pointed
|
||||
|
// by result.
|
||||
|
template <class InputIterator, class ForwardIterator> |
||||
|
static ForwardIterator _uninitialized_move_items(const InputIterator &first, |
||||
|
const InputIterator &last, const ForwardIterator &result) |
||||
|
{ |
||||
|
#ifdef GHEAP_CPP11 |
||||
|
// libstdc++ is missing std::uninitialized_move(), so wrap
|
||||
|
// the input iterator into std::make_move_iterator().
|
||||
|
// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=51981 .
|
||||
|
return std::uninitialized_copy(std::make_move_iterator(first), |
||||
|
std::make_move_iterator(last), result); |
||||
|
#else |
||||
|
return std::uninitialized_copy(first, last, result); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
// Moves items from [first ... last) to result.
|
||||
|
template <class InputIterator, class OutputIterator> |
||||
|
static OutputIterator _move_items(const InputIterator &first, |
||||
|
const InputIterator &last, const OutputIterator &result) |
||||
|
{ |
||||
|
#ifdef GHEAP_CPP11 |
||||
|
return std::move(first, last, result); |
||||
|
#else |
||||
|
return std::copy(first, last, result); |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
|
// Auxiliary function for nway_mergesort().
|
||||
|
// Splits the range [first ... last) into subranges with small_range_size size
|
||||
|
// each (except the last subrange, which may contain less
|
||||
|
// than small_range_size items) and sort each of these subranges using
|
||||
|
// small_range_sorter.
|
||||
|
template <class RandomAccessIterator, class LessComparer, |
||||
|
class SmallRangeSorter> |
||||
|
static void _sort_subranges(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer, |
||||
|
const SmallRangeSorter &small_range_sorter, |
||||
|
const size_t small_range_size) |
||||
|
{ |
||||
|
assert(first <= last); |
||||
|
assert(small_range_size > 0); |
||||
|
|
||||
|
const size_t range_size = last - first; |
||||
|
|
||||
|
const RandomAccessIterator it_last = last - range_size % small_range_size; |
||||
|
RandomAccessIterator it = first; |
||||
|
while (it != it_last) { |
||||
|
const RandomAccessIterator it_first = it; |
||||
|
it += small_range_size; |
||||
|
small_range_sorter(it_first, it, less_comparer); |
||||
|
} |
||||
|
|
||||
|
// Sort the last subrange, which contains less than small_range_size items.
|
||||
|
if (it < last) { |
||||
|
small_range_sorter(it, last, less_comparer); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Auxiliary function for nway_mergesort().
|
||||
|
// Merges subranges inside each subrange tuple.
|
||||
|
// Each subrange tuple contains subranges_count subranges, except the last
|
||||
|
// tuple, which may contain less than subranges_count subranges.
|
||||
|
// Each subrange contains subrange_size items, except the last subrange,
|
||||
|
// which may contain less than subrange_size items.
|
||||
|
template <class InputIterator, class OutputIterator, class LessComparer> |
||||
|
static void _merge_subrange_tuples(const InputIterator &first, |
||||
|
const InputIterator &last, const OutputIterator &result, |
||||
|
const LessComparer &less_comparer, |
||||
|
std::pair<InputIterator, InputIterator> *const subranges, |
||||
|
const size_t subranges_count, const size_t subrange_size) |
||||
|
{ |
||||
|
assert(first <= last); |
||||
|
assert(subranges_count > 1); |
||||
|
assert(subrange_size > 0); |
||||
|
|
||||
|
typedef std::pair<InputIterator, InputIterator> subrange_t; |
||||
|
|
||||
|
const size_t range_size = last - first; |
||||
|
InputIterator it = first; |
||||
|
OutputIterator output = result; |
||||
|
|
||||
|
// Merge full subrange tuples. Each full subrange tuple contains
|
||||
|
// subranges_count full subranges. Each full subrange contains
|
||||
|
// subrange_size items.
|
||||
|
if (subrange_size <= range_size / subranges_count) { |
||||
|
const size_t tuple_size = subrange_size * subranges_count; |
||||
|
const InputIterator it_last = last - range_size % tuple_size; |
||||
|
|
||||
|
while (it != it_last) { |
||||
|
for (size_t i = 0; i < subranges_count; ++i) { |
||||
|
const InputIterator it_first = it; |
||||
|
std::advance(it, subrange_size); |
||||
|
new (subranges + i) subrange_t(it_first, it); |
||||
|
} |
||||
|
|
||||
|
output = nway_merge(subranges, subranges + subranges_count, output, |
||||
|
less_comparer); |
||||
|
|
||||
|
for (size_t i = 0; i < subranges_count; ++i) { |
||||
|
subranges[i].~subrange_t(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Merge tail subrange tuple. Tail subrange tuple contains less than
|
||||
|
// subranges_count full subranges. It also may contain tail subrange
|
||||
|
// with less than subrange_size items.
|
||||
|
const size_t tail_tuple_size = last - it; |
||||
|
if (tail_tuple_size > 0) { |
||||
|
const size_t full_subranges_count = tail_tuple_size / subrange_size; |
||||
|
assert(full_subranges_count < subranges_count); |
||||
|
size_t tail_subranges_count = full_subranges_count; |
||||
|
|
||||
|
for (size_t i = 0; i < full_subranges_count; ++i) { |
||||
|
const InputIterator it_first = it; |
||||
|
std::advance(it, subrange_size); |
||||
|
new (subranges + i) subrange_t(it_first, it); |
||||
|
} |
||||
|
|
||||
|
if (it < last) { |
||||
|
new (subranges + full_subranges_count) subrange_t(it, last); |
||||
|
++tail_subranges_count; |
||||
|
} |
||||
|
|
||||
|
nway_merge(subranges, subranges + tail_subranges_count, output, |
||||
|
less_comparer); |
||||
|
|
||||
|
for (size_t i = 0; i < tail_subranges_count; ++i) { |
||||
|
subranges[i].~subrange_t(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
|
||||
|
// Sorts items [first ... middle) in ascending order.
|
||||
|
// Uses less_comparer for items' comparison.
|
||||
|
//
|
||||
|
// std::swap() specialization and/or move constructor/assignment
|
||||
|
// may be provided for non-trivial items as a speed optimization.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void heapsort(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
Heap::make_heap(first, last, less_comparer); |
||||
|
Heap::sort_heap(first, last, less_comparer); |
||||
|
} |
||||
|
|
||||
|
// Sorts items [first ... middle) in ascending order.
|
||||
|
// Uses operator< for items' comparison.
|
||||
|
//
|
||||
|
// std::swap() specialization and/or move constructor/assignment
|
||||
|
// may be provided for non-trivial items as a speed optimization.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void heapsort(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
heapsort(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Performs partial sort, so [first ... middle) will contain items sorted
|
||||
|
// in ascending order, which are smaller than the rest of items
|
||||
|
// in the [middle ... last).
|
||||
|
// Uses less_comparer for items' comparison.
|
||||
|
//
|
||||
|
// std::swap() specialization and/or move constructor/assignment
|
||||
|
// may be provided for non-trivial items as a speed optimization.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void partial_sort(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &middle, const RandomAccessIterator &last, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(first <= middle); |
||||
|
assert(middle <= last); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t sorted_range_size = middle - first; |
||||
|
if (sorted_range_size > 0) { |
||||
|
Heap::make_heap(first, middle, less_comparer); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
for (size_t i = sorted_range_size; i < heap_size; ++i) { |
||||
|
if (less_comparer(first[i], first[0])) { |
||||
|
Heap::swap_max_item(first, middle, first[i], less_comparer); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Heap::sort_heap(first, middle, less_comparer); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Performs partial sort, so [first ... middle) will contain items sorted
|
||||
|
// in ascending order, which are smaller than the rest of items
|
||||
|
// in the [middle ... last).
|
||||
|
// Uses operator< for items' comparison.
|
||||
|
//
|
||||
|
// std::swap() specialization and/or move constructor/assignment
|
||||
|
// may be provided for non-trivial items as a speed optimization.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void partial_sort(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &middle, const RandomAccessIterator &last) |
||||
|
{ |
||||
|
partial_sort(first, middle, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Performs N-way merging of the given input ranges into the result sorted
|
||||
|
// in ascending order, using less_comparer for items' comparison.
|
||||
|
//
|
||||
|
// Each input range must hold non-zero number of items sorted
|
||||
|
// in ascending order. Each range is defined as a std::pair containing
|
||||
|
// input iterators, where the first iterator points to the beginning
|
||||
|
// of the range, while the second iterator points to the end of the range.
|
||||
|
//
|
||||
|
// Returns an iterator pointing to the next element in the result after
|
||||
|
// the merge.
|
||||
|
//
|
||||
|
// std::swap() specialization and/or move constructor/assignment
|
||||
|
// may be provided for non-trivial input ranges as a speed optimization.
|
||||
|
//
|
||||
|
// As a side effect the function shuffles input ranges between
|
||||
|
// [input_ranges_first ... input_ranges_last) and sets the first iterator
|
||||
|
// for each input range to the end of the corresponding range.
|
||||
|
//
|
||||
|
// Also values from input ranges may become obsolete after
|
||||
|
// the funtion return, because they can be moved to the result via
|
||||
|
// move construction or move assignment in C++11.
|
||||
|
template <class RandomAccessIterator, class OutputIterator, |
||||
|
class LessComparer> |
||||
|
static OutputIterator nway_merge( |
||||
|
const RandomAccessIterator &input_ranges_first, |
||||
|
const RandomAccessIterator &input_ranges_last, |
||||
|
const OutputIterator &result, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(input_ranges_first < input_ranges_last); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
input_range_iterator; |
||||
|
|
||||
|
const RandomAccessIterator &first = input_ranges_first; |
||||
|
RandomAccessIterator last = input_ranges_last; |
||||
|
OutputIterator output = result; |
||||
|
|
||||
|
const _nway_merge_less_comparer<LessComparer> less(less_comparer); |
||||
|
|
||||
|
Heap::make_heap(first, last, less); |
||||
|
while (true) { |
||||
|
input_range_iterator &input_range = first[0]; |
||||
|
assert(input_range.first != input_range.second); |
||||
|
#ifdef GHEAP_CPP11 |
||||
|
*output = std::move(*(input_range.first)); |
||||
|
#else |
||||
|
*output = *(input_range.first); |
||||
|
#endif |
||||
|
++output; |
||||
|
++(input_range.first); |
||||
|
if (input_range.first == input_range.second) { |
||||
|
--last; |
||||
|
if (first == last) { |
||||
|
break; |
||||
|
} |
||||
|
std::swap(input_range, *last); |
||||
|
} |
||||
|
Heap::restore_heap_after_item_decrease(first, first, last, less); |
||||
|
} |
||||
|
|
||||
|
return output; |
||||
|
} |
||||
|
|
||||
|
// Performs N-way merging of the given input ranges into the result sorted
|
||||
|
// in ascending order, using operator< for items' comparison.
|
||||
|
//
|
||||
|
// Each input range must hold non-zero number of items sorted
|
||||
|
// in ascending order. Each range is defined as a std::pair containing
|
||||
|
// input iterators, where the first iterator points to the beginning
|
||||
|
// of the range, while the second iterator points to the end of the range.
|
||||
|
//
|
||||
|
// Returns an iterator pointing to the next element in the result after
|
||||
|
// the merge.
|
||||
|
//
|
||||
|
// std::swap() specialization and/or move constructor/assignment
|
||||
|
// may be provided for non-trivial input ranges as a speed optimization.
|
||||
|
//
|
||||
|
// As a side effect the function shuffles input ranges between
|
||||
|
// [input_ranges_first ... input_ranges_last) and sets the first iterator
|
||||
|
// for each input range to the end of the corresponding range.
|
||||
|
//
|
||||
|
// Also values from input ranges may become obsolete after
|
||||
|
// the function return, because they can be moved to the result via
|
||||
|
// move construction or move assignment in C++11.
|
||||
|
template <class RandomAccessIterator, class OutputIterator> |
||||
|
static OutputIterator nway_merge( |
||||
|
const RandomAccessIterator &input_ranges_first, |
||||
|
const RandomAccessIterator &input_ranges_last, |
||||
|
const OutputIterator &result) |
||||
|
{ |
||||
|
typedef typename std::iterator_traits<RandomAccessIterator |
||||
|
>::value_type::first_type input_iterator; |
||||
|
|
||||
|
return nway_merge(input_ranges_first, input_ranges_last, result, |
||||
|
_std_less_comparer<input_iterator>); |
||||
|
} |
||||
|
|
||||
|
// Performs n-way mergesort.
|
||||
|
//
|
||||
|
// Uses:
|
||||
|
// - less_comparer for items' comparison.
|
||||
|
// - small_range_sorter for sorting ranges containing no more
|
||||
|
// than small_range_size items.
|
||||
|
//
|
||||
|
// Splits the input range into subranges with small_range_size size,
|
||||
|
// sorts them using small_range_sorter and then merges them back
|
||||
|
// using n-way merge with n = subranges_count.
|
||||
|
//
|
||||
|
// items_tmp_buf must point to an uninitialized memory, which can hold
|
||||
|
// up to (last - first) items.
|
||||
|
//
|
||||
|
// May raise std::bad_alloc on unsuccessful attempt to allocate temporary
|
||||
|
// space for auxiliary structures required for n-way merging.
|
||||
|
template <class ForwardIterator, class LessComparer, class SmallRangeSorter> |
||||
|
static void nway_mergesort(const ForwardIterator &first, |
||||
|
const ForwardIterator &last, const LessComparer &less_comparer, |
||||
|
const SmallRangeSorter &small_range_sorter, |
||||
|
const size_t small_range_size, const size_t subranges_count, |
||||
|
typename std::iterator_traits<ForwardIterator>::value_type |
||||
|
*const items_tmp_buf) |
||||
|
{ |
||||
|
assert(first <= last); |
||||
|
assert(small_range_size > 0); |
||||
|
assert(subranges_count > 1); |
||||
|
|
||||
|
typedef typename std::iterator_traits<ForwardIterator>::value_type |
||||
|
value_type; |
||||
|
typedef std::pair<ForwardIterator, ForwardIterator> subrange1_t; |
||||
|
typedef std::pair<value_type *, value_type *> subrange2_t; |
||||
|
|
||||
|
const size_t range_size = last - first; |
||||
|
|
||||
|
// Preparation: Move items to a temporary buffer.
|
||||
|
_uninitialized_move_items(first, last, items_tmp_buf); |
||||
|
|
||||
|
// Step 1: split the range into subranges with small_range_size size each
|
||||
|
// (except the last subrange, which may contain less than small_range_size
|
||||
|
// items) and sort each of these subranges using small_range_sorter.
|
||||
|
_sort_subranges(items_tmp_buf, items_tmp_buf + range_size, |
||||
|
less_comparer, small_range_sorter, small_range_size); |
||||
|
|
||||
|
// Step 2: Merge subranges sorted at the previous step using n-way merge.
|
||||
|
const _temporary_buffer<subrange1_t> subranges_tmp_buf1(subranges_count); |
||||
|
const _temporary_buffer<subrange2_t> subranges_tmp_buf2(subranges_count); |
||||
|
|
||||
|
size_t subrange_size = small_range_size; |
||||
|
for (;;) { |
||||
|
// First pass: merge items from the temporary buffer
|
||||
|
// to the original location.
|
||||
|
_merge_subrange_tuples( |
||||
|
items_tmp_buf, items_tmp_buf + range_size, first, less_comparer, |
||||
|
subranges_tmp_buf2.get_ptr(), subranges_count, subrange_size); |
||||
|
|
||||
|
if (subrange_size > range_size / subranges_count) { |
||||
|
break; |
||||
|
} |
||||
|
subrange_size *= subranges_count; |
||||
|
|
||||
|
// Second pass: merge items from the original location
|
||||
|
// to the temporary buffer.
|
||||
|
_merge_subrange_tuples( |
||||
|
first, last, items_tmp_buf, less_comparer, |
||||
|
subranges_tmp_buf1.get_ptr(), subranges_count, subrange_size); |
||||
|
|
||||
|
if (subrange_size > range_size / subranges_count) { |
||||
|
// Move items from the temporary buffer to the original location.
|
||||
|
_move_items(items_tmp_buf, items_tmp_buf + range_size, first); |
||||
|
break; |
||||
|
} |
||||
|
subrange_size *= subranges_count; |
||||
|
} |
||||
|
|
||||
|
// Destroy dummy items in the temporary buffer.
|
||||
|
for (size_t i = 0; i < range_size; ++i) { |
||||
|
items_tmp_buf[i].~value_type(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Performs n-way mergesort.
|
||||
|
//
|
||||
|
// Uses:
|
||||
|
// - less_comparer for items' comparison.
|
||||
|
// - small_range_sorter for sorting ranges containing no more
|
||||
|
// than small_range_size items.
|
||||
|
//
|
||||
|
// Splits the input range into subranges with small_range_size size,
|
||||
|
// sorts them using small_range_sorter and then merges them back
|
||||
|
// using n-way merge with n = subranges_count.
|
||||
|
//
|
||||
|
// May raise std::bad_alloc on unsuccessful attempt to allocate a temporary
|
||||
|
// buffer for (last - first) items.
|
||||
|
template <class ForwardIterator, class LessComparer, class SmallRangeSorter> |
||||
|
static void nway_mergesort(const ForwardIterator &first, |
||||
|
const ForwardIterator &last, const LessComparer &less_comparer, |
||||
|
const SmallRangeSorter &small_range_sorter, |
||||
|
const size_t small_range_size = 32, const size_t subranges_count = 15) |
||||
|
{ |
||||
|
assert(first <= last); |
||||
|
|
||||
|
typedef typename std::iterator_traits<ForwardIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t range_size = last - first; |
||||
|
|
||||
|
const _temporary_buffer<value_type> tmp_buf(range_size); |
||||
|
value_type *const items_tmp_buf = tmp_buf.get_ptr(); |
||||
|
|
||||
|
nway_mergesort(first, last, less_comparer, small_range_sorter, |
||||
|
small_range_size, subranges_count, items_tmp_buf); |
||||
|
} |
||||
|
|
||||
|
// Performs n-way mergesort.
|
||||
|
//
|
||||
|
// Uses less_comparer for items' comparison.
|
||||
|
//
|
||||
|
// May raise std::bad_alloc on unsuccessful attempt to allocate a temporary
|
||||
|
// buffer for (last - first) items.
|
||||
|
template <class ForwardIterator, class LessComparer> |
||||
|
static void nway_mergesort(const ForwardIterator &first, |
||||
|
const ForwardIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
typedef typename std::iterator_traits<ForwardIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
nway_mergesort(first, last, less_comparer, |
||||
|
_std_small_range_sorter<value_type, LessComparer>); |
||||
|
} |
||||
|
|
||||
|
// Performs n-way mergesort.
|
||||
|
//
|
||||
|
// Uses operator< for items' comparison.
|
||||
|
//
|
||||
|
// May raise std::bad_alloc on unsuccessful attempt to allocate a temporary
|
||||
|
// buffer for (last - first) items.
|
||||
|
template <class ForwardIterator> |
||||
|
static void nway_mergesort(const ForwardIterator &first, |
||||
|
const ForwardIterator &last) |
||||
|
{ |
||||
|
nway_mergesort(first, last, _std_less_comparer<ForwardIterator>); |
||||
|
} |
||||
|
}; |
||||
|
#endif |
@ -0,0 +1,535 @@ |
|||||
|
#ifndef GHEAP_H |
||||
|
#define GHEAP_H |
||||
|
|
||||
|
/*
|
||||
|
* Generalized heap implementation for C99. |
||||
|
* |
||||
|
* Don't forget passing -DNDEBUG option to the compiler when creating optimized |
||||
|
* builds. This significantly speeds up gheap code by removing debug assertions. |
||||
|
* |
||||
|
* Author: Aliaksandr Valialkin <valyala@gmail.com>. |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
/*******************************************************************************
|
||||
|
* Interface. |
||||
|
******************************************************************************/ |
||||
|
|
||||
|
#include <stddef.h> /* for size_t */ |
||||
|
#include <stdint.h> /* for SIZE_MAX */ |
||||
|
|
||||
|
/*
|
||||
|
* Less comparer must return non-zero value if a < b. |
||||
|
* ctx is the gheap_ctx->less_comparer_ctx. |
||||
|
* Otherwise it must return 0. |
||||
|
*/ |
||||
|
typedef int (*gheap_less_comparer_t)(const void *ctx, const void *a, |
||||
|
const void *b); |
||||
|
|
||||
|
/*
|
||||
|
* Moves the item from src to dst. |
||||
|
*/ |
||||
|
typedef void (*gheap_item_mover_t)(void *dst, const void *src); |
||||
|
|
||||
|
/*
|
||||
|
* Gheap context. |
||||
|
* This context must be passed to every gheap function. |
||||
|
*/ |
||||
|
struct gheap_ctx |
||||
|
{ |
||||
|
/*
|
||||
|
* How much children each heap item can have. |
||||
|
*/ |
||||
|
size_t fanout; |
||||
|
|
||||
|
/*
|
||||
|
* A chunk is a tuple containing fanout items arranged sequentially in memory. |
||||
|
* A page is a subheap containing page_chunks chunks arranged sequentially |
||||
|
* in memory. |
||||
|
* The number of chunks in a page is an arbitrary integer greater than 0. |
||||
|
*/ |
||||
|
size_t page_chunks; |
||||
|
|
||||
|
/*
|
||||
|
* The size of each item in bytes. |
||||
|
*/ |
||||
|
size_t item_size; |
||||
|
|
||||
|
gheap_less_comparer_t less_comparer; |
||||
|
const void *less_comparer_ctx; |
||||
|
|
||||
|
gheap_item_mover_t item_mover; |
||||
|
}; |
||||
|
|
||||
|
/*
|
||||
|
* Returns parent index for the given child index. |
||||
|
* Child index must be greater than 0. |
||||
|
* Returns 0 if the parent is root. |
||||
|
*/ |
||||
|
static inline size_t gheap_get_parent_index(const struct gheap_ctx *ctx, |
||||
|
size_t u); |
||||
|
|
||||
|
/*
|
||||
|
* Returns the index of the first child for the given parent index. |
||||
|
* Parent index must be less than SIZE_MAX. |
||||
|
* Returns SIZE_MAX if the index of the first child for the given parent |
||||
|
* cannot fit size_t. |
||||
|
*/ |
||||
|
static inline size_t gheap_get_child_index(const struct gheap_ctx *ctx, |
||||
|
size_t u); |
||||
|
|
||||
|
/*
|
||||
|
* Returns a pointer to the first non-heap item using less_comparer |
||||
|
* for items' comparison. |
||||
|
* Returns the index of the first non-heap item. |
||||
|
* Returns heap_size if base points to valid max heap with the given size. |
||||
|
*/ |
||||
|
static inline size_t gheap_is_heap_until(const struct gheap_ctx *ctx, |
||||
|
const void *base, size_t heap_size); |
||||
|
|
||||
|
/*
|
||||
|
* Returns non-zero if base points to valid max heap. Returns zero otherwise. |
||||
|
* Uses less_comparer for items' comparison. |
||||
|
*/ |
||||
|
static inline int gheap_is_heap(const struct gheap_ctx *ctx, |
||||
|
const void *base, size_t heap_size); |
||||
|
|
||||
|
/*
|
||||
|
* Makes max heap from items base[0] ... base[heap_size-1]. |
||||
|
* Uses less_comparer for items' comparison. |
||||
|
*/ |
||||
|
static inline void gheap_make_heap(const struct gheap_ctx *ctx, |
||||
|
void *base, size_t heap_size); |
||||
|
|
||||
|
/*
|
||||
|
* Pushes the item base[heap_size-1] into max heap base[0] ... base[heap_size-2] |
||||
|
* Uses less_comparer for items' comparison. |
||||
|
*/ |
||||
|
static inline void gheap_push_heap(const struct gheap_ctx *ctx, |
||||
|
void *base, size_t heap_size); |
||||
|
|
||||
|
/*
|
||||
|
* Pops the maximum item from max heap base[0] ... base[heap_size-1] into |
||||
|
* base[heap_size-1]. |
||||
|
* Uses less_comparer for items' comparison. |
||||
|
*/ |
||||
|
static inline void gheap_pop_heap(const struct gheap_ctx *ctx, |
||||
|
void *base, size_t heap_size); |
||||
|
|
||||
|
/*
|
||||
|
* Sorts items in place of max heap in ascending order. |
||||
|
* Uses less_comparer for items' comparison. |
||||
|
*/ |
||||
|
static inline void gheap_sort_heap(const struct gheap_ctx *ctx, |
||||
|
void *base, size_t heap_size); |
||||
|
|
||||
|
/*
|
||||
|
* Swaps the item outside the heap with the maximum item inside |
||||
|
* the heap and restores heap invariant. |
||||
|
*/ |
||||
|
static inline void gheap_swap_max_item(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size, void *item); |
||||
|
|
||||
|
/*
|
||||
|
* Restores max heap invariant after item's value has been increased, |
||||
|
* i.e. less_comparer(old_item, new_item) != 0. |
||||
|
*/ |
||||
|
static inline void gheap_restore_heap_after_item_increase( |
||||
|
const struct gheap_ctx *ctx, |
||||
|
void *base, size_t heap_size, size_t modified_item_index); |
||||
|
|
||||
|
/*
|
||||
|
* Restores max heap invariant after item's value has been decreased, |
||||
|
* i.e. less_comparer(new_item, old_item) != 0. |
||||
|
*/ |
||||
|
static inline void gheap_restore_heap_after_item_decrease( |
||||
|
const struct gheap_ctx *ctx, |
||||
|
void *base, size_t heap_size, size_t modified_item_index); |
||||
|
|
||||
|
/*
|
||||
|
* Removes the given item from the heap and puts it into base[heap_size-1]. |
||||
|
* Uses less_comparer for items' comparison. |
||||
|
*/ |
||||
|
static inline void gheap_remove_from_heap(const struct gheap_ctx *ctx, |
||||
|
void *base, size_t heap_size, size_t item_index); |
||||
|
|
||||
|
/*******************************************************************************
|
||||
|
* Implementation. |
||||
|
* |
||||
|
* Define all functions inline, so compiler will be able optimizing out common |
||||
|
* args (fanout, page_chunks, item_size, less_comparer and item_mover), |
||||
|
* which are usually constants, using contant folding optimization |
||||
|
* ( http://en.wikipedia.org/wiki/Constant_folding ).
|
||||
|
*****************************************************************************/ |
||||
|
|
||||
|
#include <assert.h> /* for assert */ |
||||
|
#include <stddef.h> /* for size_t */ |
||||
|
#include <stdint.h> /* for uintptr_t, SIZE_MAX and UINTPTR_MAX */ |
||||
|
|
||||
|
static inline size_t gheap_get_parent_index(const struct gheap_ctx *const ctx, |
||||
|
size_t u) |
||||
|
{ |
||||
|
assert(u > 0); |
||||
|
|
||||
|
const size_t fanout = ctx->fanout; |
||||
|
const size_t page_chunks = ctx->page_chunks; |
||||
|
|
||||
|
--u; |
||||
|
if (page_chunks == 1) { |
||||
|
return u / fanout; |
||||
|
} |
||||
|
|
||||
|
if (u < fanout) { |
||||
|
/* Parent is root. */ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
assert(page_chunks <= SIZE_MAX / fanout); |
||||
|
const size_t page_size = fanout * page_chunks; |
||||
|
size_t v = u % page_size; |
||||
|
if (v >= fanout) { |
||||
|
/* Fast path. Parent is on the same page as the child. */ |
||||
|
return u - v + v / fanout; |
||||
|
} |
||||
|
|
||||
|
/* Slow path. Parent is on another page. */ |
||||
|
v = u / page_size - 1; |
||||
|
const size_t page_leaves = (fanout - 1) * page_chunks + 1; |
||||
|
u = v / page_leaves + 1; |
||||
|
return u * page_size + v % page_leaves - page_leaves + 1; |
||||
|
} |
||||
|
|
||||
|
static inline size_t gheap_get_child_index(const struct gheap_ctx *const ctx, |
||||
|
size_t u) |
||||
|
{ |
||||
|
assert(u < SIZE_MAX); |
||||
|
|
||||
|
const size_t fanout = ctx->fanout; |
||||
|
const size_t page_chunks = ctx->page_chunks; |
||||
|
|
||||
|
if (page_chunks == 1) { |
||||
|
if (u > (SIZE_MAX - 1) / fanout) { |
||||
|
/* Child overflow. */ |
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return u * fanout + 1; |
||||
|
} |
||||
|
|
||||
|
if (u == 0) { |
||||
|
/* Root's child is always 1. */ |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
assert(page_chunks <= SIZE_MAX / fanout); |
||||
|
const size_t page_size = fanout * page_chunks; |
||||
|
--u; |
||||
|
size_t v = u % page_size + 1; |
||||
|
if (v < page_size / fanout) { |
||||
|
/* Fast path. Child is on the same page as the parent. */ |
||||
|
v *= fanout - 1; |
||||
|
if (u > SIZE_MAX - 2 - v) { |
||||
|
/* Child overflow. */ |
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return u + v + 2; |
||||
|
} |
||||
|
|
||||
|
/* Slow path. Child is on another page. */ |
||||
|
const size_t page_leaves = (fanout - 1) * page_chunks + 1; |
||||
|
v += (u / page_size + 1) * page_leaves - page_size; |
||||
|
if (v > (SIZE_MAX - 1) / page_size) { |
||||
|
/* Child overflow. */ |
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return v * page_size + 1; |
||||
|
} |
||||
|
|
||||
|
/* Returns a pointer to base[index]. */ |
||||
|
static inline void *_gheap_get_item_ptr(const struct gheap_ctx *const ctx, |
||||
|
const void *const base, const size_t index) |
||||
|
{ |
||||
|
const size_t item_size = ctx->item_size; |
||||
|
|
||||
|
assert(index <= SIZE_MAX / item_size); |
||||
|
|
||||
|
const size_t offset = item_size * index; |
||||
|
assert((uintptr_t)base <= UINTPTR_MAX - offset); |
||||
|
|
||||
|
return ((char *)base) + offset; |
||||
|
} |
||||
|
|
||||
|
/*
|
||||
|
* Sifts the item up in the given sub-heap with the given root_index |
||||
|
* starting from the hole_index. |
||||
|
*/ |
||||
|
static inline void _gheap_sift_up(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t root_index, size_t hole_index, |
||||
|
const void *const item) |
||||
|
{ |
||||
|
assert(hole_index >= root_index); |
||||
|
|
||||
|
const gheap_less_comparer_t less_comparer = ctx->less_comparer; |
||||
|
const void *const less_comparer_ctx = ctx->less_comparer_ctx; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
while (hole_index > root_index) { |
||||
|
const size_t parent_index = gheap_get_parent_index(ctx, hole_index); |
||||
|
assert(parent_index >= root_index); |
||||
|
const void *const parent = _gheap_get_item_ptr(ctx, base, parent_index); |
||||
|
if (!less_comparer(less_comparer_ctx, parent, item)) { |
||||
|
break; |
||||
|
} |
||||
|
item_mover(_gheap_get_item_ptr(ctx, base, hole_index), |
||||
|
parent); |
||||
|
hole_index = parent_index; |
||||
|
} |
||||
|
|
||||
|
item_mover(_gheap_get_item_ptr(ctx, base, hole_index), item); |
||||
|
} |
||||
|
|
||||
|
/*
|
||||
|
* Moves the max child into the given hole and returns index |
||||
|
* of the new hole. |
||||
|
*/ |
||||
|
static inline size_t _gheap_move_up_max_child(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t children_count, |
||||
|
const size_t hole_index, const size_t child_index) |
||||
|
{ |
||||
|
assert(children_count > 0); |
||||
|
assert(children_count <= ctx->fanout); |
||||
|
assert(child_index == gheap_get_child_index(ctx, hole_index)); |
||||
|
|
||||
|
const gheap_less_comparer_t less_comparer = ctx->less_comparer; |
||||
|
const void *const less_comparer_ctx = ctx->less_comparer_ctx; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
size_t max_child_index = child_index; |
||||
|
for (size_t i = 1; i < children_count; ++i) { |
||||
|
if (!less_comparer(less_comparer_ctx, |
||||
|
_gheap_get_item_ptr(ctx, base, child_index + i), |
||||
|
_gheap_get_item_ptr(ctx, base, max_child_index))) { |
||||
|
max_child_index = child_index + i; |
||||
|
} |
||||
|
} |
||||
|
item_mover(_gheap_get_item_ptr(ctx, base, hole_index), |
||||
|
_gheap_get_item_ptr(ctx, base, max_child_index)); |
||||
|
return max_child_index; |
||||
|
} |
||||
|
|
||||
|
/*
|
||||
|
* Sifts the given item down in the heap of the given size starting |
||||
|
* from the hole_index. |
||||
|
*/ |
||||
|
static inline void _gheap_sift_down(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size, size_t hole_index, |
||||
|
const void *const item) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(hole_index < heap_size); |
||||
|
|
||||
|
const size_t fanout = ctx->fanout; |
||||
|
|
||||
|
const size_t root_index = hole_index; |
||||
|
const size_t last_full_index = heap_size - (heap_size - 1) % fanout; |
||||
|
while (1) { |
||||
|
const size_t child_index = gheap_get_child_index(ctx, hole_index); |
||||
|
if (child_index >= last_full_index) { |
||||
|
if (child_index < heap_size) { |
||||
|
assert(child_index == last_full_index); |
||||
|
hole_index = _gheap_move_up_max_child(ctx, base, |
||||
|
heap_size - child_index, hole_index, child_index); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
assert(heap_size - child_index >= fanout); |
||||
|
hole_index = _gheap_move_up_max_child(ctx, base, fanout, hole_index, |
||||
|
child_index); |
||||
|
} |
||||
|
_gheap_sift_up(ctx, base, root_index, hole_index, item); |
||||
|
} |
||||
|
|
||||
|
/*
|
||||
|
* Pops the maximum item from the heap [base[0] ... base[heap_size-1]] |
||||
|
* into base[heap_size]. |
||||
|
*/ |
||||
|
static inline void _gheap_pop_max_item(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size) |
||||
|
{ |
||||
|
void *const hole = _gheap_get_item_ptr(ctx, base, heap_size); |
||||
|
gheap_swap_max_item(ctx, base, heap_size, hole); |
||||
|
} |
||||
|
|
||||
|
static inline size_t gheap_is_heap_until(const struct gheap_ctx *const ctx, |
||||
|
const void *const base, const size_t heap_size) |
||||
|
{ |
||||
|
const gheap_less_comparer_t less_comparer = ctx->less_comparer; |
||||
|
const void *const less_comparer_ctx = ctx->less_comparer_ctx; |
||||
|
|
||||
|
for (size_t u = 1; u < heap_size; ++u) { |
||||
|
const size_t v = gheap_get_parent_index(ctx, u); |
||||
|
const void *const a = _gheap_get_item_ptr(ctx, base, v); |
||||
|
const void *const b = _gheap_get_item_ptr(ctx, base, u); |
||||
|
if (less_comparer(less_comparer_ctx, a, b)) { |
||||
|
return u; |
||||
|
} |
||||
|
} |
||||
|
return heap_size; |
||||
|
} |
||||
|
|
||||
|
static inline int gheap_is_heap(const struct gheap_ctx *const ctx, |
||||
|
const void *const base, const size_t heap_size) |
||||
|
{ |
||||
|
return (gheap_is_heap_until(ctx, base, heap_size) == heap_size); |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_make_heap(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size) |
||||
|
{ |
||||
|
const size_t fanout = ctx->fanout; |
||||
|
const size_t page_chunks = ctx->page_chunks; |
||||
|
const size_t item_size = ctx->item_size; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
if (heap_size > 1) { |
||||
|
/* Skip leaf nodes without children. This is easy to do for non-paged heap,
|
||||
|
* i.e. when page_chunks = 1, but it is difficult for paged heaps. |
||||
|
* So leaf nodes in paged heaps are visited anyway. |
||||
|
*/ |
||||
|
size_t i = (page_chunks == 1) ? ((heap_size - 2) / fanout) : |
||||
|
(heap_size - 2); |
||||
|
do { |
||||
|
char tmp[item_size]; |
||||
|
item_mover(tmp, _gheap_get_item_ptr(ctx, base, i)); |
||||
|
_gheap_sift_down(ctx, base, heap_size, i, tmp); |
||||
|
} while (i-- > 0); |
||||
|
} |
||||
|
|
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_push_heap(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(gheap_is_heap(ctx, base, heap_size - 1)); |
||||
|
|
||||
|
const size_t item_size = ctx->item_size; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
if (heap_size > 1) { |
||||
|
const size_t u = heap_size - 1; |
||||
|
char tmp[item_size]; |
||||
|
item_mover(tmp, _gheap_get_item_ptr(ctx, base, u)); |
||||
|
_gheap_sift_up(ctx, base, 0, u, tmp); |
||||
|
} |
||||
|
|
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_pop_heap(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
|
||||
|
if (heap_size > 1) { |
||||
|
_gheap_pop_max_item(ctx, base, heap_size - 1); |
||||
|
} |
||||
|
|
||||
|
assert(gheap_is_heap(ctx, base, heap_size - 1)); |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_sort_heap(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size) |
||||
|
{ |
||||
|
for (size_t i = heap_size; i > 1; --i) { |
||||
|
_gheap_pop_max_item(ctx, base, i - 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_swap_max_item(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size, void *item) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
|
||||
|
const size_t item_size = ctx->item_size; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
char tmp[item_size]; |
||||
|
item_mover(tmp, item); |
||||
|
item_mover(item, base); |
||||
|
_gheap_sift_down(ctx, base, heap_size, 0, tmp); |
||||
|
|
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_restore_heap_after_item_increase( |
||||
|
const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size, size_t modified_item_index) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(modified_item_index < heap_size); |
||||
|
assert(gheap_is_heap(ctx, base, modified_item_index)); |
||||
|
|
||||
|
const size_t item_size = ctx->item_size; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
if (modified_item_index > 0) { |
||||
|
char tmp[item_size]; |
||||
|
item_mover(tmp, _gheap_get_item_ptr(ctx, base, modified_item_index)); |
||||
|
_gheap_sift_up(ctx, base, 0, modified_item_index, tmp); |
||||
|
} |
||||
|
|
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
(void)heap_size; |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_restore_heap_after_item_decrease( |
||||
|
const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size, size_t modified_item_index) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(modified_item_index < heap_size); |
||||
|
assert(gheap_is_heap(ctx, base, modified_item_index)); |
||||
|
|
||||
|
const size_t item_size = ctx->item_size; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
char tmp[item_size]; |
||||
|
item_mover(tmp, _gheap_get_item_ptr(ctx, base, modified_item_index)); |
||||
|
_gheap_sift_down(ctx, base, heap_size, modified_item_index, tmp); |
||||
|
|
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
} |
||||
|
|
||||
|
static inline void gheap_remove_from_heap(const struct gheap_ctx *const ctx, |
||||
|
void *const base, const size_t heap_size, size_t item_index) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(item_index < heap_size); |
||||
|
assert(gheap_is_heap(ctx, base, heap_size)); |
||||
|
|
||||
|
const size_t item_size = ctx->item_size; |
||||
|
const gheap_less_comparer_t less_comparer = ctx->less_comparer; |
||||
|
const void *const less_comparer_ctx = ctx->less_comparer_ctx; |
||||
|
const gheap_item_mover_t item_mover = ctx->item_mover; |
||||
|
|
||||
|
const size_t new_heap_size = heap_size - 1; |
||||
|
if (item_index < new_heap_size) { |
||||
|
char tmp[item_size]; |
||||
|
void *const hole = _gheap_get_item_ptr(ctx, base, new_heap_size); |
||||
|
item_mover(tmp, hole); |
||||
|
item_mover(hole, _gheap_get_item_ptr(ctx, base, item_index)); |
||||
|
if (less_comparer(less_comparer_ctx, tmp, hole)) { |
||||
|
_gheap_sift_down(ctx, base, new_heap_size, item_index, tmp); |
||||
|
} |
||||
|
else { |
||||
|
_gheap_sift_up(ctx, base, 0, item_index, tmp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
assert(gheap_is_heap(ctx, base, new_heap_size)); |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,8 @@ |
|||||
|
// Pass -DGHEAP_CPP11 to compiler for including gheap optimized for C++11.
|
||||
|
// Otherwise gheap optimized for C++03 will be included.
|
||||
|
|
||||
|
#ifdef GHEAP_CPP11 |
||||
|
# include "gheap_cpp11.hpp" |
||||
|
#else |
||||
|
# include "gheap_cpp03.hpp" |
||||
|
#endif |
@ -0,0 +1,515 @@ |
|||||
|
#ifndef GHEAP_H |
||||
|
#define GHEAP_H |
||||
|
|
||||
|
// Generalized heap implementation for C++03.
|
||||
|
//
|
||||
|
// The implementation relies on std::swap<>() specializations,
|
||||
|
// so provide swap() specializations for classes with expensive copy
|
||||
|
// constructor and/or copy assignment operator.
|
||||
|
//
|
||||
|
// Use gheap_cpp11.hpp instead if your compiler supports C++11.
|
||||
|
// See http://en.wikipedia.org/wiki/C%2B%2B11 for details.
|
||||
|
// The implementation for C++11 is usually faster than the implementation
|
||||
|
// for C++03.
|
||||
|
//
|
||||
|
// Don't forget passing -DNDEBUG option to the compiler when creating optimized
|
||||
|
// builds. This significantly speeds up gheap code by removing debug assertions.
|
||||
|
//
|
||||
|
// Author: Aliaksandr Valialkin <valyala@gmail.com>.
|
||||
|
|
||||
|
#include <algorithm> // for std::swap() |
||||
|
#include <cassert> // for assert |
||||
|
#include <cstddef> // for size_t |
||||
|
#include <iterator> // for std::iterator_traits |
||||
|
|
||||
|
// C++03 has no SIZE_MAX, so define it here.
|
||||
|
#ifndef SIZE_MAX |
||||
|
# define SIZE_MAX (~(size_t)0) |
||||
|
#endif |
||||
|
|
||||
|
template <size_t Fanout = 2, size_t PageChunks = 1> |
||||
|
class gheap |
||||
|
{ |
||||
|
public: |
||||
|
|
||||
|
static const size_t FANOUT = Fanout; |
||||
|
static const size_t PAGE_CHUNKS = PageChunks; |
||||
|
static const size_t PAGE_SIZE = Fanout * PageChunks; |
||||
|
|
||||
|
// Returns parent index for the given child index.
|
||||
|
// Child index must be greater than 0.
|
||||
|
// Returns 0 if the parent is root.
|
||||
|
static size_t get_parent_index(size_t u) |
||||
|
{ |
||||
|
assert(u > 0); |
||||
|
|
||||
|
--u; |
||||
|
if (PageChunks == 1) { |
||||
|
return u / Fanout; |
||||
|
} |
||||
|
|
||||
|
if (u < Fanout) { |
||||
|
// Parent is root.
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
assert(PageChunks <= SIZE_MAX / Fanout); |
||||
|
const size_t page_size = Fanout * PageChunks; |
||||
|
size_t v = u % page_size; |
||||
|
if (v >= Fanout) { |
||||
|
// Fast path. Parent is on the same page as the child.
|
||||
|
return u - v + v / Fanout; |
||||
|
} |
||||
|
|
||||
|
// Slow path. Parent is on another page.
|
||||
|
v = u / page_size - 1; |
||||
|
const size_t page_leaves = (Fanout - 1) * PageChunks + 1; |
||||
|
u = v / page_leaves + 1; |
||||
|
return u * page_size + v % page_leaves - page_leaves + 1; |
||||
|
} |
||||
|
|
||||
|
// Returns the index of the first child for the given parent index.
|
||||
|
// Parent index must be less than SIZE_MAX.
|
||||
|
// Returns SIZE_MAX if the index of the first child for the given parent
|
||||
|
// cannot fit size_t.
|
||||
|
static size_t get_child_index(size_t u) |
||||
|
{ |
||||
|
assert(u < SIZE_MAX); |
||||
|
|
||||
|
if (PageChunks == 1) { |
||||
|
if (u > (SIZE_MAX - 1) / Fanout) { |
||||
|
// Child overflow.
|
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return u * Fanout + 1; |
||||
|
} |
||||
|
|
||||
|
if (u == 0) { |
||||
|
// Root's child is always 1.
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
assert(PageChunks <= SIZE_MAX / Fanout); |
||||
|
const size_t page_size = Fanout * PageChunks; |
||||
|
--u; |
||||
|
size_t v = u % page_size + 1; |
||||
|
if (v < page_size / Fanout) { |
||||
|
// Fast path. Child is on the same page as the parent.
|
||||
|
v *= Fanout - 1; |
||||
|
if (u > SIZE_MAX - 2 - v) { |
||||
|
// Child overflow.
|
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return u + v + 2; |
||||
|
} |
||||
|
|
||||
|
// Slow path. Child is on another page.
|
||||
|
const size_t page_leaves = (Fanout - 1) * PageChunks + 1; |
||||
|
v += (u / page_size + 1) * page_leaves - page_size; |
||||
|
if (v > (SIZE_MAX - 1) / page_size) { |
||||
|
// Child overflow.
|
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return v * page_size + 1; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
|
||||
|
template <class T> |
||||
|
static void _swap(const T &a, const T &b) |
||||
|
{ |
||||
|
// a and b are const for optimization purposes only. This hints compiler
|
||||
|
// that values referenced by a and b cannot be modified by somebody else,
|
||||
|
// so it is safe reading these values from CPU registers instead of reading
|
||||
|
// them from slow memory on each read access.
|
||||
|
//
|
||||
|
// Of course, this optimization works only if values are small enough
|
||||
|
// to fit CPU registers.
|
||||
|
std::swap(const_cast<T &>(a), const_cast<T &>(b)); |
||||
|
} |
||||
|
|
||||
|
// Sifts the item up in the given sub-heap with the given root_index
|
||||
|
// starting from the item_index.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void _sift_up(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, |
||||
|
const size_t root_index, size_t item_index) |
||||
|
{ |
||||
|
assert(item_index >= root_index); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
while (item_index > root_index) { |
||||
|
const size_t parent_index = get_parent_index(item_index); |
||||
|
assert(parent_index >= root_index); |
||||
|
const value_type &item = first[item_index]; |
||||
|
const value_type &parent = first[parent_index]; |
||||
|
if (!less_comparer(parent, item)) { |
||||
|
break; |
||||
|
} |
||||
|
_swap(item, parent); |
||||
|
item_index = parent_index; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Swaps the max child with the item at item_index and returns index
|
||||
|
// of the max child.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static size_t _move_up_max_child(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, const size_t children_count, |
||||
|
const size_t item_index, const size_t child_index) |
||||
|
{ |
||||
|
assert(children_count > 0); |
||||
|
assert(children_count <= Fanout); |
||||
|
assert(child_index == get_child_index(item_index)); |
||||
|
|
||||
|
size_t max_child_index = child_index; |
||||
|
for (size_t i = 1; i < children_count; ++i) { |
||||
|
if (!less_comparer(first[child_index + i], first[max_child_index])) { |
||||
|
max_child_index = child_index + i; |
||||
|
} |
||||
|
} |
||||
|
_swap(first[item_index], first[max_child_index]); |
||||
|
return max_child_index; |
||||
|
} |
||||
|
|
||||
|
// Sifts the given item down in the heap of the given size starting
|
||||
|
// from the item_index.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void _sift_down(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, |
||||
|
const size_t heap_size, size_t item_index) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(item_index < heap_size); |
||||
|
|
||||
|
const size_t root_index = item_index; |
||||
|
const size_t last_full_index = heap_size - (heap_size - 1) % Fanout; |
||||
|
while (true) { |
||||
|
const size_t child_index = get_child_index(item_index); |
||||
|
if (child_index >= last_full_index) { |
||||
|
if (child_index < heap_size) { |
||||
|
assert(child_index == last_full_index); |
||||
|
item_index = _move_up_max_child(first, less_comparer, |
||||
|
heap_size - child_index, item_index, child_index); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
assert(heap_size - child_index >= Fanout); |
||||
|
item_index = _move_up_max_child(first, less_comparer, Fanout, |
||||
|
item_index, child_index); |
||||
|
} |
||||
|
_sift_up(first, less_comparer, root_index, item_index); |
||||
|
} |
||||
|
|
||||
|
// Standard less comparer.
|
||||
|
template <class InputIterator> |
||||
|
static bool _std_less_comparer( |
||||
|
const typename std::iterator_traits<InputIterator>::value_type &a, |
||||
|
const typename std::iterator_traits<InputIterator>::value_type &b) |
||||
|
{ |
||||
|
return (a < b); |
||||
|
} |
||||
|
|
||||
|
// Pops max item from the heap [first[0] ... first[heap_size-1]]
|
||||
|
// into first[heap_size].
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void _pop_max_item(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, const size_t heap_size) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
|
||||
|
_swap(first[heap_size], first[0]); |
||||
|
_sift_down(first, less_comparer, heap_size, 0); |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
|
||||
|
// Returns an iterator for the first non-heap item in the range
|
||||
|
// [first ... last) using less_comparer for items' comparison.
|
||||
|
// Returns last if the range contains valid max heap.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static RandomAccessIterator is_heap_until( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &last, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last >= first); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
for (size_t u = 1; u < heap_size; ++u) { |
||||
|
const size_t v = get_parent_index(u); |
||||
|
if (less_comparer(first[v], first[u])) { |
||||
|
return first + u; |
||||
|
} |
||||
|
} |
||||
|
return last; |
||||
|
} |
||||
|
|
||||
|
// Returns an iterator for the first non-heap item in the range
|
||||
|
// [first ... last) using operator< for items' comparison.
|
||||
|
// Returns last if the range contains valid max heap.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static RandomAccessIterator is_heap_until( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &last) |
||||
|
{ |
||||
|
return is_heap_until(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Returns true if the range [first ... last) contains valid max heap.
|
||||
|
// Returns false otherwise.
|
||||
|
// Uses less_comparer for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static bool is_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
return (is_heap_until(first, last, less_comparer) == last); |
||||
|
} |
||||
|
|
||||
|
// Returns true if the range [first ... last) contains valid max heap.
|
||||
|
// Returns false otherwise.
|
||||
|
// Uses operator< for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static bool is_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
return is_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Makes max heap from items [first ... last) using the given less_comparer
|
||||
|
// for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void make_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last >= first); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
if (heap_size > 1) { |
||||
|
// Skip leaf nodes without children. This is easy to do for non-paged
|
||||
|
// heap, i.e. when page_chunks = 1, but it is difficult for paged heaps.
|
||||
|
// So leaf nodes in paged heaps are visited anyway.
|
||||
|
size_t i = (PageChunks == 1) ? ((heap_size - 2) / Fanout) : |
||||
|
(heap_size - 2); |
||||
|
do { |
||||
|
_sift_down(first, less_comparer, heap_size, i); |
||||
|
} while (i-- > 0); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Makes max heap from items [first ... last) using operator< for items'
|
||||
|
// comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void make_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
make_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Pushes the item *(last - 1) into max heap [first ... last - 1)
|
||||
|
// using the given less_comparer for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void push_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(is_heap(first, last - 1, less_comparer)); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
if (heap_size > 1) { |
||||
|
const size_t u = heap_size - 1; |
||||
|
_sift_up(first, less_comparer, 0, u); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Pushes the item *(last - 1) into max heap [first ... last - 1)
|
||||
|
// using operator< for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void push_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
push_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Pops the maximum item from max heap [first ... last) into
|
||||
|
// *(last - 1) using the given less_comparer for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void pop_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
if (heap_size > 1) { |
||||
|
_pop_max_item(first, less_comparer, heap_size - 1); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last - 1, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Pops the maximum item from max heap [first ... last) into
|
||||
|
// *(last - 1) using operator< for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void pop_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
pop_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Sorts max heap [first ... last) using the given less_comparer
|
||||
|
// for items' comparison.
|
||||
|
// Items are sorted in ascending order.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void sort_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last >= first); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
for (size_t i = heap_size; i > 1; --i) { |
||||
|
_pop_max_item(first, less_comparer, i - 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Sorts max heap [first ... last) using operator< for items' comparison.
|
||||
|
// Items are sorted in ascending order.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void sort_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
sort_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Swaps the item outside the heap with the maximum item inside
|
||||
|
// the heap [first ... last) and restores the heap invariant.
|
||||
|
// Uses less_comparer for items' comparisons.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void swap_max_item(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, |
||||
|
typename std::iterator_traits<RandomAccessIterator>::value_type &item, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(first < last); |
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
|
||||
|
_swap(item, first[0]); |
||||
|
_sift_down(first, less_comparer, heap_size, 0); |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Swaps the item outside the heap with the maximum item inside
|
||||
|
// the heap [first ... last) and restores the heap invariant.
|
||||
|
// Uses operator< for items' comparisons.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void swap_max_item(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, |
||||
|
typename std::iterator_traits<RandomAccessIterator>::value_type &item) |
||||
|
{ |
||||
|
swap_max_item(first, last, item, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been increased,
|
||||
|
// i.e. less_comparer(old_item, new_item) == true.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void restore_heap_after_item_increase( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(item >= first); |
||||
|
assert(is_heap(first, item, less_comparer)); |
||||
|
|
||||
|
const size_t item_index = item - first; |
||||
|
if (item_index > 0) { |
||||
|
_sift_up(first, less_comparer, 0, item_index); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, item + 1, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been increased,
|
||||
|
// i.e. old_item < new_item.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void restore_heap_after_item_increase( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item) |
||||
|
{ |
||||
|
restore_heap_after_item_increase(first, item, |
||||
|
_std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been decreased,
|
||||
|
// i.e. less_comparer(new_item, old_item) == true.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void restore_heap_after_item_decrease( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(item >= first); |
||||
|
assert(item < last); |
||||
|
assert(is_heap(first, item, less_comparer)); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
const size_t item_index = item - first; |
||||
|
_sift_down(first, less_comparer, heap_size, item_index); |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been decreased,
|
||||
|
// i.e. new_item < old_item.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void restore_heap_after_item_decrease( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
restore_heap_after_item_decrease(first, item, last, |
||||
|
_std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Removes the given item from the heap and puts it into *(last - 1).
|
||||
|
// less_comparer is used for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void remove_from_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &item, const RandomAccessIterator &last, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(item >= first); |
||||
|
assert(item < last); |
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
|
||||
|
const size_t new_heap_size = last - first - 1; |
||||
|
const size_t item_index = item - first; |
||||
|
if (item_index < new_heap_size) { |
||||
|
_swap(*item, first[new_heap_size]); |
||||
|
if (less_comparer(*item, first[new_heap_size])) { |
||||
|
_sift_down(first, less_comparer, new_heap_size, item_index); |
||||
|
} |
||||
|
else { |
||||
|
_sift_up(first, less_comparer, 0, item_index); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last - 1, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Removes the given item from the heap and puts it into *(last - 1).
|
||||
|
// operator< is used for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void remove_from_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &item, const RandomAccessIterator &last) |
||||
|
{ |
||||
|
remove_from_heap(first, item, last, |
||||
|
_std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
#endif |
@ -0,0 +1,544 @@ |
|||||
|
#ifndef GHEAP_H |
||||
|
#define GHEAP_H |
||||
|
|
||||
|
// Generalized heap implementation for C++11.
|
||||
|
// The implementation requires the following C++11 features:
|
||||
|
// - <cstdint> must contain SIZE_MAX definition.
|
||||
|
// - std::move() support. The implementation relies on move constructors
|
||||
|
// and move assignment operators, so define them for classes with expensive
|
||||
|
// copy constructors and copy assignment operators.
|
||||
|
// See http://en.wikipedia.org/wiki/C%2B%2B11 for details.
|
||||
|
//
|
||||
|
// Use gheap_cpp03.hpp instead if your compiler doesn't support these features.
|
||||
|
// The implementation for C++11 is usually faster than the implementation
|
||||
|
// for C++03.
|
||||
|
//
|
||||
|
// Don't forget passing -DNDEBUG option to the compiler when creating optimized
|
||||
|
// builds. This significantly speeds up gheap code by removing debug assertions.
|
||||
|
//
|
||||
|
// Author: Aliaksandr Valialkin <valyala@gmail.com>.
|
||||
|
|
||||
|
#include <cassert> // for assert |
||||
|
#include <cstddef> // for size_t |
||||
|
#include <cstdint> // for SIZE_MAX |
||||
|
#include <iterator> // for std::iterator_traits |
||||
|
#include <utility> // for std::move() |
||||
|
|
||||
|
template <size_t Fanout = 2, size_t PageChunks = 1> |
||||
|
class gheap |
||||
|
{ |
||||
|
public: |
||||
|
|
||||
|
static const size_t FANOUT = Fanout; |
||||
|
static const size_t PAGE_CHUNKS = PageChunks; |
||||
|
static const size_t PAGE_SIZE = Fanout * PageChunks; |
||||
|
|
||||
|
// Returns parent index for the given child index.
|
||||
|
// Child index must be greater than 0.
|
||||
|
// Returns 0 if the parent is root.
|
||||
|
static size_t get_parent_index(size_t u) |
||||
|
{ |
||||
|
assert(u > 0); |
||||
|
|
||||
|
--u; |
||||
|
if (PageChunks == 1) { |
||||
|
return u / Fanout; |
||||
|
} |
||||
|
|
||||
|
if (u < Fanout) { |
||||
|
// Parent is root.
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
assert(PageChunks <= SIZE_MAX / Fanout); |
||||
|
const size_t page_size = Fanout * PageChunks; |
||||
|
size_t v = u % page_size; |
||||
|
if (v >= Fanout) { |
||||
|
// Fast path. Parent is on the same page as the child.
|
||||
|
return u - v + v / Fanout; |
||||
|
} |
||||
|
|
||||
|
// Slow path. Parent is on another page.
|
||||
|
v = u / page_size - 1; |
||||
|
const size_t page_leaves = (Fanout - 1) * PageChunks + 1; |
||||
|
u = v / page_leaves + 1; |
||||
|
return u * page_size + v % page_leaves - page_leaves + 1; |
||||
|
} |
||||
|
|
||||
|
// Returns the index of the first child for the given parent index.
|
||||
|
// Parent index must be less than SIZE_MAX.
|
||||
|
// Returns SIZE_MAX if the index of the first child for the given parent
|
||||
|
// cannot fit size_t.
|
||||
|
static size_t get_child_index(size_t u) |
||||
|
{ |
||||
|
assert(u < SIZE_MAX); |
||||
|
|
||||
|
if (PageChunks == 1) { |
||||
|
if (u > (SIZE_MAX - 1) / Fanout) { |
||||
|
// Child overflow.
|
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return u * Fanout + 1; |
||||
|
} |
||||
|
|
||||
|
if (u == 0) { |
||||
|
// Root's child is always 1.
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
assert(PageChunks <= SIZE_MAX / Fanout); |
||||
|
const size_t page_size = Fanout * PageChunks; |
||||
|
--u; |
||||
|
size_t v = u % page_size + 1; |
||||
|
if (v < page_size / Fanout) { |
||||
|
// Fast path. Child is on the same page as the parent.
|
||||
|
v *= Fanout - 1; |
||||
|
if (u > SIZE_MAX - 2 - v) { |
||||
|
// Child overflow.
|
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return u + v + 2; |
||||
|
} |
||||
|
|
||||
|
// Slow path. Child is on another page.
|
||||
|
const size_t page_leaves = (Fanout - 1) * PageChunks + 1; |
||||
|
v += (u / page_size + 1) * page_leaves - page_size; |
||||
|
if (v > (SIZE_MAX - 1) / page_size) { |
||||
|
// Child overflow.
|
||||
|
return SIZE_MAX; |
||||
|
} |
||||
|
return v * page_size + 1; |
||||
|
} |
||||
|
|
||||
|
private: |
||||
|
|
||||
|
// moves the value from src to dst.
|
||||
|
template <class T> |
||||
|
static void _move(T &dst, const T &src) |
||||
|
{ |
||||
|
// src is const for optimization purposes only. This hints compiler
|
||||
|
// that the value referenced by src cannot be modified by somebody else,
|
||||
|
// so it is safe reading the value from a register instead of reading it
|
||||
|
// from slow memory on each read access.
|
||||
|
//
|
||||
|
// Of course, this optimization works only for values small enough to fit
|
||||
|
// CPU registers.
|
||||
|
dst = std::move(const_cast<T &>(src)); |
||||
|
} |
||||
|
|
||||
|
// Sifts the item up in the given sub-heap with the given root_index
|
||||
|
// starting from the hole_index.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void _sift_up(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, |
||||
|
const size_t root_index, size_t hole_index, |
||||
|
const typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
&item) |
||||
|
{ |
||||
|
assert(hole_index >= root_index); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
while (hole_index > root_index) { |
||||
|
const size_t parent_index = get_parent_index(hole_index); |
||||
|
assert(parent_index >= root_index); |
||||
|
const value_type &parent = first[parent_index]; |
||||
|
if (!less_comparer(parent, item)) { |
||||
|
break; |
||||
|
} |
||||
|
_move(first[hole_index], parent); |
||||
|
hole_index = parent_index; |
||||
|
} |
||||
|
_move(first[hole_index], item); |
||||
|
} |
||||
|
|
||||
|
// Moves the max child into the given hole and returns index
|
||||
|
// of the new hole.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static size_t _move_up_max_child(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, const size_t children_count, |
||||
|
const size_t hole_index, const size_t child_index) |
||||
|
{ |
||||
|
assert(children_count > 0); |
||||
|
assert(children_count <= Fanout); |
||||
|
assert(child_index == get_child_index(hole_index)); |
||||
|
|
||||
|
size_t max_child_index = child_index; |
||||
|
for (size_t i = 1; i < children_count; ++i) { |
||||
|
if (!less_comparer(first[child_index + i], first[max_child_index])) { |
||||
|
max_child_index = child_index + i; |
||||
|
} |
||||
|
} |
||||
|
_move(first[hole_index], first[max_child_index]); |
||||
|
return max_child_index; |
||||
|
} |
||||
|
|
||||
|
// Sifts the given item down in the heap of the given size starting
|
||||
|
// from the hole_index.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void _sift_down(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, |
||||
|
const size_t heap_size, size_t hole_index, |
||||
|
const typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
&item) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
assert(hole_index < heap_size); |
||||
|
|
||||
|
const size_t root_index = hole_index; |
||||
|
const size_t last_full_index = heap_size - (heap_size - 1) % Fanout; |
||||
|
while (true) { |
||||
|
const size_t child_index = get_child_index(hole_index); |
||||
|
if (child_index >= last_full_index) { |
||||
|
if (child_index < heap_size) { |
||||
|
assert(child_index == last_full_index); |
||||
|
hole_index = _move_up_max_child(first, less_comparer, |
||||
|
heap_size - child_index, hole_index, child_index); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
assert(heap_size - child_index >= Fanout); |
||||
|
hole_index = _move_up_max_child(first, less_comparer, Fanout, |
||||
|
hole_index, child_index); |
||||
|
} |
||||
|
_sift_up(first, less_comparer, root_index, hole_index, item); |
||||
|
} |
||||
|
|
||||
|
// Standard less comparer.
|
||||
|
template <class InputIterator> |
||||
|
static bool _std_less_comparer( |
||||
|
const typename std::iterator_traits<InputIterator>::value_type &a, |
||||
|
const typename std::iterator_traits<InputIterator>::value_type &b) |
||||
|
{ |
||||
|
return (a < b); |
||||
|
} |
||||
|
|
||||
|
// Pops max item from the heap [first[0] ... first[heap_size-1]]
|
||||
|
// into first[heap_size].
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void _pop_max_item(const RandomAccessIterator &first, |
||||
|
const LessComparer &less_comparer, const size_t heap_size) |
||||
|
{ |
||||
|
assert(heap_size > 0); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
value_type tmp = std::move(first[heap_size]); |
||||
|
_move(first[heap_size], first[0]); |
||||
|
_sift_down(first, less_comparer, heap_size, 0, tmp); |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
|
||||
|
// Returns an iterator for the first non-heap item in the range
|
||||
|
// [first ... last) using less_comparer for items' comparison.
|
||||
|
// Returns last if the range contains valid max heap.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static RandomAccessIterator is_heap_until( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &last, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last >= first); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
for (size_t u = 1; u < heap_size; ++u) { |
||||
|
const size_t v = get_parent_index(u); |
||||
|
if (less_comparer(first[v], first[u])) { |
||||
|
return first + u; |
||||
|
} |
||||
|
} |
||||
|
return last; |
||||
|
} |
||||
|
|
||||
|
// Returns an iterator for the first non-heap item in the range
|
||||
|
// [first ... last) using operator< for items' comparison.
|
||||
|
// Returns last if the range contains valid max heap.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static RandomAccessIterator is_heap_until( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &last) |
||||
|
{ |
||||
|
return is_heap_until(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Returns true if the range [first ... last) contains valid max heap.
|
||||
|
// Returns false otherwise.
|
||||
|
// Uses less_comparer for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static bool is_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
return (is_heap_until(first, last, less_comparer) == last); |
||||
|
} |
||||
|
|
||||
|
// Returns true if the range [first ... last) contains valid max heap.
|
||||
|
// Returns false otherwise.
|
||||
|
// Uses operator< for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static bool is_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
return is_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Makes max heap from items [first ... last) using the given less_comparer
|
||||
|
// for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void make_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last >= first); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
if (heap_size > 1) { |
||||
|
// Skip leaf nodes without children. This is easy to do for non-paged
|
||||
|
// heap, i.e. when page_chunks = 1, but it is difficult for paged heaps.
|
||||
|
// So leaf nodes in paged heaps are visited anyway.
|
||||
|
size_t i = (PageChunks == 1) ? ((heap_size - 2) / Fanout) : |
||||
|
(heap_size - 2); |
||||
|
do { |
||||
|
value_type item = std::move(first[i]); |
||||
|
_sift_down(first, less_comparer, heap_size, i, item); |
||||
|
} while (i-- > 0); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Makes max heap from items [first ... last) using operator< for items'
|
||||
|
// comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void make_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
make_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Pushes the item *(last - 1) into max heap [first ... last - 1)
|
||||
|
// using the given less_comparer for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void push_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(is_heap(first, last - 1, less_comparer)); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
if (heap_size > 1) { |
||||
|
const size_t u = heap_size - 1; |
||||
|
value_type item = std::move(first[u]); |
||||
|
_sift_up(first, less_comparer, 0, u, item); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Pushes the item *(last - 1) into max heap [first ... last - 1)
|
||||
|
// using operator< for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void push_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
push_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Pops the maximum item from max heap [first ... last) into
|
||||
|
// *(last - 1) using the given less_comparer for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void pop_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
if (heap_size > 1) { |
||||
|
_pop_max_item(first, less_comparer, heap_size - 1); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last - 1, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Pops the maximum item from max heap [first ... last) into
|
||||
|
// *(last - 1) using operator< for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void pop_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
pop_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Sorts max heap [first ... last) using the given less_comparer
|
||||
|
// for items' comparison.
|
||||
|
// Items are sorted in ascending order.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void sort_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last >= first); |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
for (size_t i = heap_size; i > 1; --i) { |
||||
|
_pop_max_item(first, less_comparer, i - 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Sorts max heap [first ... last) using operator< for items' comparison.
|
||||
|
// Items are sorted in ascending order.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void sort_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
sort_heap(first, last, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Swaps the item outside the heap with the maximum item inside
|
||||
|
// the heap [first ... last) and restores the heap invariant.
|
||||
|
// Uses less_comparer for items' comparisons.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void swap_max_item(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, |
||||
|
typename std::iterator_traits<RandomAccessIterator>::value_type &item, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(first < last); |
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
|
||||
|
value_type tmp = std::move(item); |
||||
|
_move(item, first[0]); |
||||
|
_sift_down(first, less_comparer, heap_size, 0, tmp); |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Swaps the item outside the heap with the maximum item inside
|
||||
|
// the heap [first ... last) and restores the heap invariant.
|
||||
|
// Uses operator< for items' comparisons.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void swap_max_item(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, |
||||
|
typename std::iterator_traits<RandomAccessIterator>::value_type &item) |
||||
|
{ |
||||
|
swap_max_item(first, last, item, _std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been increased,
|
||||
|
// i.e. less_comparer(old_item, new_item) == true.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void restore_heap_after_item_increase( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(item >= first); |
||||
|
assert(is_heap(first, item, less_comparer)); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t hole_index = item - first; |
||||
|
if (hole_index > 0) { |
||||
|
value_type tmp = std::move(*item); |
||||
|
_sift_up(first, less_comparer, 0, hole_index, tmp); |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, item + 1, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been increased,
|
||||
|
// i.e. old_item < new_item.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void restore_heap_after_item_increase( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item) |
||||
|
{ |
||||
|
restore_heap_after_item_increase(first, item, |
||||
|
_std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been decreased,
|
||||
|
// i.e. less_comparer(new_item, old_item) == true.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void restore_heap_after_item_decrease( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(item >= first); |
||||
|
assert(item < last); |
||||
|
assert(is_heap(first, item, less_comparer)); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t heap_size = last - first; |
||||
|
const size_t hole_index = item - first; |
||||
|
value_type tmp = std::move(*item); |
||||
|
_sift_down(first, less_comparer, heap_size, hole_index, tmp); |
||||
|
|
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Restores max heap invariant after item's value has been decreased,
|
||||
|
// i.e. new_item < old_item.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void restore_heap_after_item_decrease( |
||||
|
const RandomAccessIterator &first, const RandomAccessIterator &item, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
restore_heap_after_item_decrease(first, item, last, |
||||
|
_std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
|
||||
|
// Removes the given item from the heap and puts it into *(last - 1).
|
||||
|
// less_comparer is used for items' comparison.
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void remove_from_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &item, const RandomAccessIterator &last, |
||||
|
const LessComparer &less_comparer) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
assert(item >= first); |
||||
|
assert(item < last); |
||||
|
assert(is_heap(first, last, less_comparer)); |
||||
|
|
||||
|
typedef typename std::iterator_traits<RandomAccessIterator>::value_type |
||||
|
value_type; |
||||
|
|
||||
|
const size_t new_heap_size = last - first - 1; |
||||
|
const size_t hole_index = item - first; |
||||
|
if (hole_index < new_heap_size) { |
||||
|
value_type tmp = std::move(first[new_heap_size]); |
||||
|
_move(first[new_heap_size], *item); |
||||
|
if (less_comparer(tmp, first[new_heap_size])) { |
||||
|
_sift_down(first, less_comparer, new_heap_size, hole_index, tmp); |
||||
|
} |
||||
|
else { |
||||
|
_sift_up(first, less_comparer, 0, hole_index, tmp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
assert(is_heap(first, last - 1, less_comparer)); |
||||
|
} |
||||
|
|
||||
|
// Removes the given item from the heap and puts it into *(last - 1).
|
||||
|
// operator< is used for items' comparison.
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void remove_from_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &item, const RandomAccessIterator &last) |
||||
|
{ |
||||
|
remove_from_heap(first, item, last, |
||||
|
_std_less_comparer<RandomAccessIterator>); |
||||
|
} |
||||
|
}; |
||||
|
#endif |
@ -0,0 +1,192 @@ |
|||||
|
/* Priority queue on top of gheap. */ |
||||
|
|
||||
|
/******************************************************************************
|
||||
|
* Interface. |
||||
|
*****************************************************************************/ |
||||
|
|
||||
|
#include "gheap.h" |
||||
|
|
||||
|
#include <stddef.h> /* for size_t */ |
||||
|
|
||||
|
|
||||
|
/*
|
||||
|
* Deletes the given item. |
||||
|
*/ |
||||
|
typedef void (*gpriority_queue_item_deleter_t)(void *); |
||||
|
|
||||
|
/*
|
||||
|
* Opaque type for priority queue. |
||||
|
*/ |
||||
|
struct gpriority_queue; |
||||
|
|
||||
|
/*
|
||||
|
* Creates an empty priority queue. |
||||
|
* |
||||
|
* The gheap context pointed by ctx must remain valid until |
||||
|
* gpriority_queue_delete() call. |
||||
|
*/ |
||||
|
static inline struct gpriority_queue *gpriority_queue_create( |
||||
|
const struct gheap_ctx *ctx, gpriority_queue_item_deleter_t item_deleter); |
||||
|
|
||||
|
/*
|
||||
|
* Creates a pirority queue and copies items from the given array into |
||||
|
* the priority queue. |
||||
|
*/ |
||||
|
static inline struct gpriority_queue *gpriority_queue_create_from_array( |
||||
|
const struct gheap_ctx *ctx, gpriority_queue_item_deleter_t item_deleter, |
||||
|
const void *a, size_t n); |
||||
|
|
||||
|
/*
|
||||
|
* Deletes the given priority queue. |
||||
|
*/ |
||||
|
static inline void gpriority_queue_delete(struct gpriority_queue *q); |
||||
|
|
||||
|
/*
|
||||
|
* Returns non-zero if the given priority queue is empty. |
||||
|
* Otherwise returns zero. |
||||
|
*/ |
||||
|
static inline int gpriority_queue_empty(struct gpriority_queue *q); |
||||
|
|
||||
|
/*
|
||||
|
* Returns the size of the given priority queue. |
||||
|
*/ |
||||
|
static inline size_t gpriority_queue_size(struct gpriority_queue *q); |
||||
|
|
||||
|
/*
|
||||
|
* Returns a pointer to the top element in the priority queue. |
||||
|
*/ |
||||
|
static inline const void *gpriority_queue_top(struct gpriority_queue *q); |
||||
|
|
||||
|
/*
|
||||
|
* Pushes a copy of the given item into priority queue. |
||||
|
*/ |
||||
|
static inline void gpriority_queue_push(struct gpriority_queue *q, |
||||
|
const void *item); |
||||
|
|
||||
|
/*
|
||||
|
* Pops the top element from the priority queue. |
||||
|
*/ |
||||
|
static inline void gpriority_queue_pop(struct gpriority_queue *q); |
||||
|
|
||||
|
|
||||
|
/******************************************************************************
|
||||
|
* Implementation. |
||||
|
*****************************************************************************/ |
||||
|
|
||||
|
#include <stdint.h> /* for SIZE_MAX */ |
||||
|
#include <stdio.h> /* for fprintf() */ |
||||
|
#include <stdlib.h> /* for malloc(), free() */ |
||||
|
|
||||
|
struct gpriority_queue |
||||
|
{ |
||||
|
const struct gheap_ctx *ctx; |
||||
|
gpriority_queue_item_deleter_t item_deleter; |
||||
|
|
||||
|
void *base; |
||||
|
size_t size; |
||||
|
size_t capacity; |
||||
|
}; |
||||
|
|
||||
|
static inline struct gpriority_queue *gpriority_queue_create( |
||||
|
const struct gheap_ctx *const ctx, |
||||
|
const gpriority_queue_item_deleter_t item_deleter) |
||||
|
{ |
||||
|
struct gpriority_queue *q = malloc(sizeof(*q)); |
||||
|
|
||||
|
q->ctx = ctx; |
||||
|
q->item_deleter = item_deleter; |
||||
|
|
||||
|
q->base = malloc(ctx->item_size); |
||||
|
q->size = 0; |
||||
|
q->capacity = 1; |
||||
|
|
||||
|
return q; |
||||
|
} |
||||
|
|
||||
|
static inline struct gpriority_queue *gpriority_queue_create_from_array( |
||||
|
const struct gheap_ctx *const ctx, |
||||
|
const gpriority_queue_item_deleter_t item_deleter, |
||||
|
const void *const a, size_t n) |
||||
|
{ |
||||
|
struct gpriority_queue *q = malloc(sizeof(*q)); |
||||
|
|
||||
|
q->ctx = ctx; |
||||
|
q->item_deleter = item_deleter; |
||||
|
|
||||
|
assert(n <= SIZE_MAX / ctx->item_size); |
||||
|
q->base = malloc(n * ctx->item_size); |
||||
|
q->size = n; |
||||
|
q->capacity = n; |
||||
|
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const void *const src = ((char *)a) + i * ctx->item_size; |
||||
|
void *const dst = ((char *)q->base) + i * ctx->item_size; |
||||
|
ctx->item_mover(dst, src); |
||||
|
} |
||||
|
gheap_make_heap(ctx, q->base, q->size); |
||||
|
|
||||
|
return q; |
||||
|
} |
||||
|
|
||||
|
static inline void gpriority_queue_delete(struct gpriority_queue *const q) |
||||
|
{ |
||||
|
for (size_t i = 0; i < q->size; ++i) { |
||||
|
void *const item = ((char *)q->base) + i * q->ctx->item_size; |
||||
|
q->item_deleter(item); |
||||
|
} |
||||
|
free(q->base); |
||||
|
free(q); |
||||
|
} |
||||
|
|
||||
|
static inline int gpriority_queue_empty(struct gpriority_queue *const q) |
||||
|
{ |
||||
|
return (q->size == 0); |
||||
|
} |
||||
|
|
||||
|
static inline size_t gpriority_queue_size(struct gpriority_queue *const q) |
||||
|
{ |
||||
|
return q->size; |
||||
|
} |
||||
|
|
||||
|
static inline const void *gpriority_queue_top(struct gpriority_queue *const q) |
||||
|
{ |
||||
|
assert(q->size > 0); |
||||
|
|
||||
|
return q->base; |
||||
|
} |
||||
|
|
||||
|
static inline void gpriority_queue_push(struct gpriority_queue *const q, |
||||
|
const void *item) |
||||
|
{ |
||||
|
if (q->size == q->capacity) { |
||||
|
if (q->capacity > SIZE_MAX / 2 / q->ctx->item_size) { |
||||
|
fprintf(stderr, "priority queue size overflow"); |
||||
|
exit(EXIT_FAILURE); |
||||
|
} |
||||
|
q->capacity *= 2; |
||||
|
char *const new_base = malloc(q->capacity * q->ctx->item_size); |
||||
|
for (size_t i = 0; i < q->size; ++i) { |
||||
|
void *const dst = new_base + i * q->ctx->item_size; |
||||
|
const void *const src = ((char *)q->base) + i * q->ctx->item_size; |
||||
|
q->ctx->item_mover(dst, src); |
||||
|
} |
||||
|
free(q->base); |
||||
|
q->base = new_base; |
||||
|
} |
||||
|
|
||||
|
assert(q->size < q->capacity); |
||||
|
void *const dst = ((char *)q->base) + q->size * q->ctx->item_size; |
||||
|
q->ctx->item_mover(dst, item); |
||||
|
++(q->size); |
||||
|
gheap_push_heap(q->ctx, q->base, q->size); |
||||
|
} |
||||
|
|
||||
|
static inline void gpriority_queue_pop(struct gpriority_queue *const q) |
||||
|
{ |
||||
|
assert(q->size > 0); |
||||
|
|
||||
|
gheap_pop_heap(q->ctx, q->base, q->size); |
||||
|
--(q->size); |
||||
|
void *const item = ((char *)q->base) + q->size * q->ctx->item_size; |
||||
|
q->item_deleter(item); |
||||
|
} |
@ -0,0 +1,124 @@ |
|||||
|
// Priority queue on top of Heap.
|
||||
|
//
|
||||
|
// Pass -DGHEAP_CPP11 to compiler for enabling C++11 optimization,
|
||||
|
// otherwise C++03 optimization will be enabled.
|
||||
|
|
||||
|
#include <cassert> |
||||
|
#include <functional> // for std::less |
||||
|
#include <vector> |
||||
|
|
||||
|
#ifdef GHEAP_CPP11 |
||||
|
# include <utility> // for std::swap(), std::move(), std::forward()
|
||||
|
#else |
||||
|
# include <algorithm> // for std::swap()
|
||||
|
#endif |
||||
|
|
||||
|
template <class Heap, class T, class Container = std::vector<T>, |
||||
|
class LessComparer = std::less<typename Container::value_type> > |
||||
|
struct gpriority_queue |
||||
|
{ |
||||
|
public: |
||||
|
|
||||
|
typedef Container container_type; |
||||
|
typedef typename Container::value_type value_type; |
||||
|
typedef typename Container::size_type size_type; |
||||
|
typedef typename Container::reference reference; |
||||
|
typedef typename Container::const_reference const_reference; |
||||
|
|
||||
|
LessComparer comp; |
||||
|
Container c; |
||||
|
|
||||
|
private: |
||||
|
|
||||
|
void _make_heap() |
||||
|
{ |
||||
|
Heap::make_heap(c.begin(), c.end(), comp); |
||||
|
} |
||||
|
|
||||
|
void _push_heap() |
||||
|
{ |
||||
|
Heap::push_heap(c.begin(), c.end(), comp); |
||||
|
} |
||||
|
|
||||
|
void _pop_heap() |
||||
|
{ |
||||
|
Heap::pop_heap(c.begin(), c.end(), comp); |
||||
|
} |
||||
|
|
||||
|
public: |
||||
|
explicit gpriority_queue( |
||||
|
const LessComparer &less_comparer = LessComparer(), |
||||
|
const Container &container = Container()) : |
||||
|
comp(less_comparer), c(container) |
||||
|
{ |
||||
|
_make_heap(); |
||||
|
} |
||||
|
|
||||
|
template <class InputIterator> |
||||
|
gpriority_queue(const InputIterator &first, const InputIterator &last, |
||||
|
const LessComparer &less_comparer = LessComparer(), |
||||
|
const Container &container = Container()) : |
||||
|
comp(less_comparer), c(container) |
||||
|
{ |
||||
|
c.insert(c.end(), first, last); |
||||
|
_make_heap(); |
||||
|
} |
||||
|
|
||||
|
bool empty() const |
||||
|
{ |
||||
|
return c.empty(); |
||||
|
} |
||||
|
|
||||
|
size_type size() const |
||||
|
{ |
||||
|
return c.size(); |
||||
|
} |
||||
|
|
||||
|
const_reference top() const |
||||
|
{ |
||||
|
assert(!empty()); |
||||
|
|
||||
|
return c.front(); |
||||
|
} |
||||
|
|
||||
|
void push(const T &v) |
||||
|
{ |
||||
|
c.push_back(v); |
||||
|
_push_heap(); |
||||
|
} |
||||
|
|
||||
|
void pop() |
||||
|
{ |
||||
|
assert(!empty()); |
||||
|
|
||||
|
_pop_heap(); |
||||
|
c.pop_back(); |
||||
|
} |
||||
|
|
||||
|
void swap(gpriority_queue &q) |
||||
|
{ |
||||
|
std::swap(c, q.c); |
||||
|
std::swap(comp, q.comp); |
||||
|
} |
||||
|
|
||||
|
#ifdef GHEAP_CPP11 |
||||
|
void push(T &&v) |
||||
|
{ |
||||
|
c.push_back(std::move(v)); |
||||
|
_push_heap(); |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
// Copy constructors and assignment operators are implicitly defined.
|
||||
|
}; |
||||
|
|
||||
|
namespace std |
||||
|
{ |
||||
|
template <class Heap, class T, class Container, class LessComparer> |
||||
|
void swap( |
||||
|
gpriority_queue<Heap, T, Container, LessComparer> &a, |
||||
|
gpriority_queue<Heap, T, Container, LessComparer> &b) |
||||
|
{ |
||||
|
a.swap(b); |
||||
|
} |
||||
|
} |
@ -0,0 +1,469 @@ |
|||||
|
// Compares the number of operations with items in gheap-based algorithms
|
||||
|
// to the number of operations with items in the corresponding STL algorithms.
|
||||
|
//
|
||||
|
// Pass -DNDEBUG for eliminating operations related to debugging checks.
|
||||
|
|
||||
|
#include "galgorithm.hpp" |
||||
|
#include "gheap.hpp" |
||||
|
|
||||
|
#include <algorithm> |
||||
|
#include <cassert> |
||||
|
#include <cstddef> |
||||
|
#include <cstdlib> |
||||
|
#include <iostream> |
||||
|
#include <iterator> |
||||
|
#include <list> |
||||
|
#include <stdint.h> // for uintptr_t (<cstdint> is missing in C++03). |
||||
|
#include <vector> |
||||
|
|
||||
|
using namespace std; |
||||
|
|
||||
|
// Simulates a LRU list of pages, which can contain up to _max_lru_size entries.
|
||||
|
// Each page's size is PAGE_MASK + 1 bytes.
|
||||
|
struct lru |
||||
|
{ |
||||
|
typedef list<uintptr_t> lru_t; |
||||
|
|
||||
|
static const uintptr_t PAGE_MASK = (((uintptr_t)1) << 12) - 1; |
||||
|
|
||||
|
// Maximum number of pages in LRU list.
|
||||
|
static const size_t MAX_LRU_SIZE = 20; |
||||
|
|
||||
|
// LRU list of pages. Front of the list contains least recently used pages.
|
||||
|
static lru_t lru_pages; |
||||
|
|
||||
|
// The number of simulated pagefaults since the last lru::init() call.
|
||||
|
static int pagefaults; |
||||
|
|
||||
|
// Resets the model to initial state.
|
||||
|
static void reset() |
||||
|
{ |
||||
|
lru_pages.clear(); |
||||
|
pagefaults = 0; |
||||
|
} |
||||
|
|
||||
|
// Simulates access to a memory pointed by ptr.
|
||||
|
// Brings the accessed page to the back of LRU list.
|
||||
|
// If the page is absent in LRU list, then increments pagefaults counter.
|
||||
|
static void access_ptr(const void *const ptr) |
||||
|
{ |
||||
|
assert(lru_pages.size() <= MAX_LRU_SIZE); |
||||
|
|
||||
|
const uintptr_t page_num = ((uintptr_t)ptr) & ~PAGE_MASK; |
||||
|
lru_t::iterator it = find(lru_pages.begin(), lru_pages.end(), |
||||
|
page_num); |
||||
|
if (it == lru_pages.end()) { |
||||
|
const uintptr_t prev_page_num = page_num - PAGE_MASK - 1; |
||||
|
if (count(lru_pages.begin(), lru_pages.end(), prev_page_num) == 0) { |
||||
|
// Count pagefault only if the previous page is not in the LRU list.
|
||||
|
// If the previous page is in the LRU list, then assume that the current
|
||||
|
// page is already pre-fetched, so no hard pagefault.
|
||||
|
++pagefaults; |
||||
|
} |
||||
|
|
||||
|
lru_pages.push_front(page_num); |
||||
|
if (lru_pages.size() > MAX_LRU_SIZE) { |
||||
|
lru_pages.pop_back(); |
||||
|
} |
||||
|
assert(lru_pages.size() <= MAX_LRU_SIZE); |
||||
|
} |
||||
|
else { |
||||
|
lru_pages.splice(lru_pages.begin(), lru_pages, it); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
lru::lru_t lru::lru_pages; |
||||
|
int lru::pagefaults = 0; |
||||
|
|
||||
|
|
||||
|
struct A |
||||
|
{ |
||||
|
static int default_ctors; |
||||
|
static int copy_ctors; |
||||
|
static int copy_assignments; |
||||
|
static int swaps; |
||||
|
static int cheap_dtors; |
||||
|
static int expensive_dtors; |
||||
|
static int move_ctors; |
||||
|
static int cheap_move_assignments; |
||||
|
static int expensive_move_assignments; |
||||
|
static int comparisons; |
||||
|
|
||||
|
static void reset() |
||||
|
{ |
||||
|
default_ctors = 0; |
||||
|
copy_ctors = 0; |
||||
|
copy_assignments = 0; |
||||
|
swaps = 0; |
||||
|
cheap_dtors = 0; |
||||
|
expensive_dtors = 0; |
||||
|
move_ctors = 0; |
||||
|
cheap_move_assignments = 0; |
||||
|
expensive_move_assignments = 0; |
||||
|
comparisons = 0; |
||||
|
lru::reset(); |
||||
|
} |
||||
|
|
||||
|
static void print() |
||||
|
{ |
||||
|
cout << "default_ctors=" << default_ctors << ", copy_ctors=" << |
||||
|
copy_ctors << ", copy_assignments=" << copy_assignments << |
||||
|
", swaps=" << swaps << ", cheap_dtors=" << cheap_dtors << |
||||
|
", expensive_dtors=" << expensive_dtors << ", move_ctors=" << |
||||
|
move_ctors << ", cheap_move_assignments=" << cheap_move_assignments << |
||||
|
", expensive_move_assignments=" << expensive_move_assignments << |
||||
|
", comparisons=" << comparisons << ", pagefaults=" << lru::pagefaults << |
||||
|
endl; |
||||
|
} |
||||
|
|
||||
|
int value; |
||||
|
|
||||
|
bool has_value() const |
||||
|
{ |
||||
|
return (value >= 0); |
||||
|
} |
||||
|
|
||||
|
int get_value() const |
||||
|
{ |
||||
|
assert(has_value()); |
||||
|
lru::access_ptr(this); |
||||
|
return value; |
||||
|
} |
||||
|
|
||||
|
void set_value(const int v) |
||||
|
{ |
||||
|
assert(v >= 0); |
||||
|
value = v; |
||||
|
lru::access_ptr(this); |
||||
|
} |
||||
|
|
||||
|
void set_value(const A &a) |
||||
|
{ |
||||
|
int v = a.get_value(); |
||||
|
set_value(v); |
||||
|
} |
||||
|
|
||||
|
void clear_value() |
||||
|
{ |
||||
|
value = -1; |
||||
|
lru::access_ptr(this); |
||||
|
} |
||||
|
|
||||
|
A() |
||||
|
{ |
||||
|
++default_ctors; |
||||
|
clear_value(); |
||||
|
} |
||||
|
|
||||
|
A(const int v) |
||||
|
{ |
||||
|
set_value(v); |
||||
|
} |
||||
|
|
||||
|
A(const A &a) |
||||
|
{ |
||||
|
++copy_ctors; |
||||
|
set_value(a); |
||||
|
} |
||||
|
|
||||
|
void operator = (const A &a) |
||||
|
{ |
||||
|
if (this == &a) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
assert(has_value()); |
||||
|
++copy_assignments; |
||||
|
set_value(a); |
||||
|
} |
||||
|
|
||||
|
~A() |
||||
|
{ |
||||
|
if (has_value()) { |
||||
|
++expensive_dtors; |
||||
|
} |
||||
|
else { |
||||
|
++cheap_dtors; |
||||
|
} |
||||
|
clear_value(); |
||||
|
} |
||||
|
|
||||
|
#ifdef GHEAP_CPP11 |
||||
|
|
||||
|
A(A &&a) |
||||
|
{ |
||||
|
++move_ctors; |
||||
|
set_value(a); |
||||
|
a.clear_value(); |
||||
|
} |
||||
|
|
||||
|
void operator = (A &&a) |
||||
|
{ |
||||
|
if (this == &a) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (has_value()) { |
||||
|
++expensive_move_assignments; |
||||
|
} |
||||
|
else { |
||||
|
++cheap_move_assignments; |
||||
|
} |
||||
|
|
||||
|
set_value(a); |
||||
|
a.clear_value(); |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
}; |
||||
|
|
||||
|
int A::default_ctors; |
||||
|
int A::copy_ctors; |
||||
|
int A::copy_assignments; |
||||
|
int A::swaps; |
||||
|
int A::cheap_dtors; |
||||
|
int A::expensive_dtors; |
||||
|
int A::move_ctors; |
||||
|
int A::cheap_move_assignments; |
||||
|
int A::expensive_move_assignments; |
||||
|
int A::comparisons; |
||||
|
|
||||
|
namespace std |
||||
|
{ |
||||
|
template <> |
||||
|
void swap(A &a, A &b) |
||||
|
{ |
||||
|
++A::swaps; |
||||
|
|
||||
|
int tmp = a.get_value(); |
||||
|
a.set_value(b); |
||||
|
b.set_value(tmp); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool operator < (const A &a, const A &b) |
||||
|
{ |
||||
|
++A::comparisons; |
||||
|
return (a.get_value() < b.get_value()); |
||||
|
} |
||||
|
|
||||
|
struct stl |
||||
|
{ |
||||
|
static const char *name() |
||||
|
{ |
||||
|
return "stl"; |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void push_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
::std::push_heap(first, last); |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void pop_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
::std::pop_heap(first, last); |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void make_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
::std::make_heap(first, last); |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void sort_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
::std::sort_heap(first, last); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
struct gtl |
||||
|
{ |
||||
|
typedef gheap<> heap; |
||||
|
|
||||
|
static const char *name() |
||||
|
{ |
||||
|
return "gheap<>"; |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void push_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
heap::push_heap(first, last); |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void pop_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
heap::pop_heap(first, last); |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void make_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
heap::make_heap(first, last); |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
static void sort_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
heap::sort_heap(first, last); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
void init_array(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
a.clear(); |
||||
|
srand(0); |
||||
|
generate_n(back_inserter(a), n, rand); |
||||
|
} |
||||
|
|
||||
|
template <class Heap> |
||||
|
void test_push_heap(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_push_heap(" << Heap::name() << "): "; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
A::reset(); |
||||
|
for (size_t i = 2; i <= n; ++i) { |
||||
|
Heap::push_heap(a.begin(), a.begin() + i); |
||||
|
} |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
template <class Heap> |
||||
|
void test_pop_heap(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_pop_heap(" << Heap::name() << "): "; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
|
||||
|
A::reset(); |
||||
|
for (size_t i = 0; i < n - 1; ++i) { |
||||
|
Heap::pop_heap(a.begin(), a.end() - i); |
||||
|
} |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
template <class Heap> |
||||
|
void test_make_heap(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_make_heap(" << Heap::name() << "): "; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
A::reset(); |
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
template <class Heap> |
||||
|
void test_sort_heap(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_sort_heap(" << Heap::name() << "): "; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
|
||||
|
A::reset(); |
||||
|
Heap::sort_heap(a.begin(), a.end()); |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
void test_nway_mergesort_avg(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_nway_mergesort_avg(" << gtl::name() << "): "; |
||||
|
|
||||
|
typedef galgorithm<gtl::heap> algorithm; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
A::reset(); |
||||
|
algorithm::nway_mergesort(a.begin(), a.end()); |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
void test_nway_mergesort_worst(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_nway_mergesort_worst(" << gtl::name() << "): "; |
||||
|
|
||||
|
typedef galgorithm<gtl::heap> algorithm; |
||||
|
|
||||
|
// Simulate worst case for SGI STL sort implementation (aka introsort) -
|
||||
|
// see http://en.wikipedia.org/wiki/Introsort .
|
||||
|
// Actually n-way mergesort must be free of bad cases.
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = n - i; |
||||
|
} |
||||
|
|
||||
|
init_array(a, n); |
||||
|
A::reset(); |
||||
|
algorithm::nway_mergesort(a.begin(), a.end()); |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
void test_sort_avg(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_sort_avg(" << stl::name() << "): "; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
A::reset(); |
||||
|
sort(a.begin(), a.end()); |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
void test_sort_worst(vector<A> &a, const size_t n) |
||||
|
{ |
||||
|
cout << " test_sort_worst(" << stl::name() << "): "; |
||||
|
|
||||
|
// Simulate worst case for SGI STL sort implementation (aka introsort) -
|
||||
|
// see http://en.wikipedia.org/wiki/Introsort .
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = n - i; |
||||
|
} |
||||
|
|
||||
|
A::reset(); |
||||
|
sort(a.begin(), a.end()); |
||||
|
A::print(); |
||||
|
} |
||||
|
|
||||
|
} // end of anonymous namespace
|
||||
|
|
||||
|
int main() |
||||
|
{ |
||||
|
const size_t N = 1000000; |
||||
|
|
||||
|
cout << "N=" << N << endl; |
||||
|
|
||||
|
vector<A> a; |
||||
|
a.reserve(N); |
||||
|
|
||||
|
test_push_heap<stl>(a, N); |
||||
|
test_push_heap<gtl>(a, N); |
||||
|
|
||||
|
test_pop_heap<stl>(a, N); |
||||
|
test_pop_heap<gtl>(a, N); |
||||
|
|
||||
|
test_make_heap<stl>(a, N); |
||||
|
test_make_heap<gtl>(a, N); |
||||
|
|
||||
|
test_sort_heap<stl>(a, N); |
||||
|
test_sort_heap<gtl>(a, N); |
||||
|
|
||||
|
test_nway_mergesort_avg(a, N); |
||||
|
test_nway_mergesort_worst(a, N); |
||||
|
test_sort_avg(a, N); |
||||
|
test_sort_worst(a, N); |
||||
|
} |
@ -0,0 +1,185 @@ |
|||||
|
#include "galgorithm.h" |
||||
|
#include "gheap.h" |
||||
|
#include "gpriority_queue.h" |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include <stdio.h> // for printf() |
||||
|
#include <stdlib.h> // for rand(), srand() |
||||
|
#include <time.h> // for clock() |
||||
|
|
||||
|
typedef size_t T; |
||||
|
|
||||
|
static int less(const void *const ctx, const void *const a, const void *const b) |
||||
|
{ |
||||
|
(void)ctx; |
||||
|
return *((T *)a) < *((T *)b); |
||||
|
} |
||||
|
|
||||
|
static void move(void *const dst, const void *const src) |
||||
|
{ |
||||
|
*((T *)dst) = *((T *)src); |
||||
|
} |
||||
|
|
||||
|
static double get_time(void) |
||||
|
{ |
||||
|
return (double)clock() / CLOCKS_PER_SEC; |
||||
|
} |
||||
|
|
||||
|
static void print_performance(const double t, const size_t m) |
||||
|
{ |
||||
|
printf(": %.0lf Kops/s\n", m / t / 1000); |
||||
|
} |
||||
|
|
||||
|
static void init_array(T *const a, const size_t n) |
||||
|
{ |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = rand(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void perftest_heapsort(const struct gheap_ctx *const ctx, |
||||
|
T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
printf("perftest_heapsort(n=%zu, m=%zu)", n, m); |
||||
|
|
||||
|
double total_time = 0; |
||||
|
|
||||
|
for (size_t i = 0; i < m / n; ++i) { |
||||
|
init_array(a, n); |
||||
|
|
||||
|
const double start = get_time(); |
||||
|
galgorithm_heapsort(ctx, a, n); |
||||
|
const double end = get_time(); |
||||
|
|
||||
|
total_time += end - start; |
||||
|
} |
||||
|
|
||||
|
print_performance(total_time, m); |
||||
|
} |
||||
|
|
||||
|
static void perftest_partial_sort(const struct gheap_ctx *const ctx, |
||||
|
T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
const size_t k = n / 4; |
||||
|
|
||||
|
printf("perftest_partial_sort(n=%zu, m=%zu, k=%zu)", n, m, k); |
||||
|
|
||||
|
double total_time = 0; |
||||
|
|
||||
|
for (size_t i = 0; i < m / n; ++i) { |
||||
|
init_array(a, n); |
||||
|
|
||||
|
const double start = get_time(); |
||||
|
galgorithm_partial_sort(ctx, a, n, k); |
||||
|
const double end = get_time(); |
||||
|
|
||||
|
total_time += end - start; |
||||
|
} |
||||
|
|
||||
|
print_performance(total_time, m); |
||||
|
} |
||||
|
|
||||
|
static void small_range_sorter(const void *const ctx, void *const a, |
||||
|
const size_t n) |
||||
|
{ |
||||
|
galgorithm_heapsort(ctx, a, n); |
||||
|
} |
||||
|
|
||||
|
static void perftest_nway_mergesort(const struct gheap_ctx *const ctx, |
||||
|
T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
const size_t small_range_size = ((1 << 20) - 1) / 3; |
||||
|
const size_t subranges_count = 15; |
||||
|
|
||||
|
printf("perftest_nway_mergesort(n=%zu, m=%zu, small_range_size=%zu, " |
||||
|
"subranges_count=%zu)", n, m, small_range_size, subranges_count); |
||||
|
|
||||
|
double total_time = 0; |
||||
|
|
||||
|
struct gheap_ctx small_range_sorter_ctx = *ctx; |
||||
|
small_range_sorter_ctx.fanout = 4; |
||||
|
|
||||
|
for (size_t i = 0; i < m / n; ++i) { |
||||
|
init_array(a, n); |
||||
|
|
||||
|
const double start = get_time(); |
||||
|
T *const items_tmp_buf = malloc(sizeof(items_tmp_buf[0]) * n); |
||||
|
galgorithm_nway_mergesort(ctx, a, n, |
||||
|
&small_range_sorter, &small_range_sorter_ctx, |
||||
|
small_range_size, subranges_count, items_tmp_buf); |
||||
|
free(items_tmp_buf); |
||||
|
const double end = get_time(); |
||||
|
|
||||
|
total_time += end - start; |
||||
|
} |
||||
|
|
||||
|
print_performance(total_time, m); |
||||
|
} |
||||
|
|
||||
|
static void delete_item(void *item) |
||||
|
{ |
||||
|
/* do nothing */ |
||||
|
(void)item; |
||||
|
} |
||||
|
|
||||
|
static void perftest_priority_queue(const struct gheap_ctx *const ctx, |
||||
|
T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
printf("perftest_priority_queue(n=%zu, m=%zu)", n, m); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
struct gpriority_queue *const q = gpriority_queue_create_from_array( |
||||
|
ctx, &delete_item, a, n); |
||||
|
|
||||
|
double start = get_time(); |
||||
|
for (size_t i = 0; i < m; ++i) { |
||||
|
gpriority_queue_pop(q); |
||||
|
const T tmp = rand(); |
||||
|
gpriority_queue_push(q, &tmp); |
||||
|
} |
||||
|
double end = get_time(); |
||||
|
|
||||
|
gpriority_queue_delete(q); |
||||
|
|
||||
|
print_performance(end - start, m); |
||||
|
} |
||||
|
|
||||
|
static void perftest(const struct gheap_ctx *const ctx, T *const a, |
||||
|
const size_t max_n) |
||||
|
{ |
||||
|
size_t n = max_n; |
||||
|
while (n > 0) { |
||||
|
perftest_heapsort(ctx, a, n, max_n); |
||||
|
perftest_partial_sort(ctx, a, n, max_n); |
||||
|
perftest_nway_mergesort(ctx, a, n, max_n); |
||||
|
perftest_priority_queue(ctx, a, n, max_n); |
||||
|
|
||||
|
n >>= 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static const struct gheap_ctx ctx_v = { |
||||
|
.fanout = 2, |
||||
|
.page_chunks = 1, |
||||
|
.item_size = sizeof(T), |
||||
|
.less_comparer = &less, |
||||
|
.less_comparer_ctx = NULL, |
||||
|
.item_mover = &move, |
||||
|
}; |
||||
|
|
||||
|
int main(void) |
||||
|
{ |
||||
|
static const size_t MAX_N = 32 * 1024 * 1024; |
||||
|
|
||||
|
printf("fanout=%zu, page_chunks=%zu, max_n=%zu\n", |
||||
|
ctx_v.fanout, ctx_v.page_chunks, MAX_N); |
||||
|
|
||||
|
srand(0); |
||||
|
T *const a = malloc(sizeof(a[0]) * MAX_N); |
||||
|
|
||||
|
perftest(&ctx_v, a, MAX_N); |
||||
|
|
||||
|
free(a); |
||||
|
|
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,226 @@ |
|||||
|
// Pass -DGHEAP_CPP11 to compiler for gheap_cpp11.hpp tests,
|
||||
|
// otherwise gheap_cpp03.hpp will be tested.
|
||||
|
|
||||
|
#include "galgorithm.hpp" |
||||
|
#include "gheap.hpp" |
||||
|
#include "gpriority_queue.hpp" |
||||
|
|
||||
|
#include <algorithm> // for *_heap(), copy() |
||||
|
#include <cstdlib> // for rand(), srand() |
||||
|
#include <ctime> // for clock() |
||||
|
#include <iostream> |
||||
|
#include <queue> // for priority_queue |
||||
|
#include <utility> // for pair |
||||
|
#include <vector> // for vector |
||||
|
|
||||
|
using namespace std; |
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
double get_time() |
||||
|
{ |
||||
|
return (double)clock() / CLOCKS_PER_SEC; |
||||
|
} |
||||
|
|
||||
|
void print_performance(const double t, const size_t m) |
||||
|
{ |
||||
|
cout << ": " << (m / t / 1000) << " Kops/s" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class T> |
||||
|
void init_array(T *const a, const size_t n) |
||||
|
{ |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = rand(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Dummy wrapper for STL heap.
|
||||
|
struct stl_heap |
||||
|
{ |
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void make_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
std::make_heap(first, last, less_comparer); |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator, class LessComparer> |
||||
|
static void sort_heap(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last, const LessComparer &less_comparer) |
||||
|
{ |
||||
|
std::sort_heap(first, last, less_comparer); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// Dummy wrapper for STL algorithms.
|
||||
|
struct stl_algorithm |
||||
|
{ |
||||
|
template <class RandomAccessIterator> |
||||
|
static void partial_sort(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &middle, const RandomAccessIterator &last) |
||||
|
{ |
||||
|
std::partial_sort(first, middle, last); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
template <class T, class Heap> |
||||
|
void perftest_heapsort(T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
cout << "perftest_heapsort(n=" << n << ", m=" << m << ")"; |
||||
|
|
||||
|
typedef galgorithm<Heap> algorithm; |
||||
|
|
||||
|
double total_time = 0; |
||||
|
|
||||
|
for (size_t i = 0; i < m / n; ++i) { |
||||
|
init_array(a, n); |
||||
|
|
||||
|
const double start = get_time(); |
||||
|
algorithm::heapsort(a, a + n); |
||||
|
const double end = get_time(); |
||||
|
|
||||
|
total_time += end - start; |
||||
|
} |
||||
|
|
||||
|
print_performance(total_time, m); |
||||
|
} |
||||
|
|
||||
|
template <class T, class Algorithm> |
||||
|
void perftest_partial_sort(T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
const size_t k = n / 4; |
||||
|
|
||||
|
cout << "perftest_partial_sort(n=" << n << ", m=" << m << ", k=" << k << ")"; |
||||
|
|
||||
|
double total_time = 0; |
||||
|
|
||||
|
for (size_t i = 0; i < m / n; ++i) { |
||||
|
init_array(a, n); |
||||
|
|
||||
|
const double start = get_time(); |
||||
|
Algorithm::partial_sort(a, a + k, a + n); |
||||
|
const double end = get_time(); |
||||
|
|
||||
|
total_time += end - start; |
||||
|
} |
||||
|
|
||||
|
print_performance(total_time, m); |
||||
|
} |
||||
|
|
||||
|
template <class T> |
||||
|
bool less_comparer(const T &a, const T &b) |
||||
|
{ |
||||
|
return (a < b); |
||||
|
} |
||||
|
|
||||
|
template <class T> |
||||
|
void small_range_sorter(T *const first, T *const last, |
||||
|
bool (&less_comparer)(const T &, const T &)) |
||||
|
{ |
||||
|
galgorithm<gheap<2, 1> >::heapsort(first, last, less_comparer); |
||||
|
} |
||||
|
|
||||
|
template <class T, class Heap> |
||||
|
void perftest_nway_mergesort(T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
const size_t small_range_size = (1 << 15) - 1; |
||||
|
const size_t subranges_count = 7; |
||||
|
|
||||
|
cout << "perftest_nway_mergesort(n=" << n << ", m=" << m << |
||||
|
", small_range_size=" << small_range_size << ", subranges_count=" << |
||||
|
subranges_count << ")"; |
||||
|
|
||||
|
typedef galgorithm<Heap> algorithm; |
||||
|
|
||||
|
double total_time = 0; |
||||
|
|
||||
|
for (size_t i = 0; i < m / n; ++i) { |
||||
|
init_array(a, n); |
||||
|
|
||||
|
const double start = get_time(); |
||||
|
algorithm::nway_mergesort(a, a + n, |
||||
|
less_comparer<T>, small_range_sorter<T>, |
||||
|
small_range_size, subranges_count); |
||||
|
const double end = get_time(); |
||||
|
|
||||
|
total_time += end - start; |
||||
|
} |
||||
|
|
||||
|
print_performance(total_time, m); |
||||
|
} |
||||
|
|
||||
|
template <class T, class PriorityQueue> |
||||
|
void perftest_priority_queue(T *const a, const size_t n, const size_t m) |
||||
|
{ |
||||
|
cout << "perftest_priority_queue(n=" << n << ", m=" << m << ")"; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
PriorityQueue q(a, a + n); |
||||
|
|
||||
|
const double start = get_time(); |
||||
|
for (size_t i = 0; i < m; ++i) { |
||||
|
q.pop(); |
||||
|
q.push(rand()); |
||||
|
} |
||||
|
const double end = get_time(); |
||||
|
|
||||
|
print_performance(end - start, m); |
||||
|
} |
||||
|
|
||||
|
template <class T, class Heap> |
||||
|
void perftest_gheap(T *const a, const size_t max_n) |
||||
|
{ |
||||
|
size_t n = max_n; |
||||
|
while (n > 0) { |
||||
|
perftest_heapsort<T, Heap>(a, n, max_n); |
||||
|
perftest_partial_sort<T, galgorithm<Heap> >(a, n, max_n); |
||||
|
perftest_nway_mergesort<T, Heap>(a, n, max_n); |
||||
|
perftest_priority_queue<T, gpriority_queue<Heap, T> >(a, n, max_n); |
||||
|
|
||||
|
n >>= 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <class T> |
||||
|
void perftest_stl_heap(T *const a, const size_t max_n) |
||||
|
{ |
||||
|
size_t n = max_n; |
||||
|
while (n > 0) { |
||||
|
perftest_heapsort<T, stl_heap>(a, n, max_n); |
||||
|
perftest_partial_sort<T, stl_algorithm>(a, n, max_n); |
||||
|
|
||||
|
// stl heap doesn't provide nway_merge(),
|
||||
|
// so skip perftest_nway_mergesort().
|
||||
|
|
||||
|
perftest_priority_queue<T, priority_queue<T> >(a, n, max_n); |
||||
|
|
||||
|
n >>= 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} // end of anonymous namespace.
|
||||
|
|
||||
|
|
||||
|
int main(void) |
||||
|
{ |
||||
|
static const size_t MAX_N = 32 * 1024 * 1024; |
||||
|
static const size_t FANOUT = 2; |
||||
|
static const size_t PAGE_CHUNKS = 1; |
||||
|
typedef size_t T; |
||||
|
|
||||
|
cout << "fanout=" << FANOUT << ", page_chunks=" << PAGE_CHUNKS << |
||||
|
", max_n=" << MAX_N << endl; |
||||
|
|
||||
|
srand(0); |
||||
|
T *const a = new T[MAX_N]; |
||||
|
|
||||
|
cout << "* STL heap" << endl; |
||||
|
perftest_stl_heap(a, MAX_N); |
||||
|
|
||||
|
cout << "* gheap" << endl; |
||||
|
typedef gheap<FANOUT, PAGE_CHUNKS> heap; |
||||
|
perftest_gheap<T, heap>(a, MAX_N); |
||||
|
|
||||
|
delete[] a; |
||||
|
} |
@ -0,0 +1,685 @@ |
|||||
|
/* Tests for C99 gheap, galgorithm and gpriority_queue */ |
||||
|
|
||||
|
#include "galgorithm.h" |
||||
|
#include "gheap.h" |
||||
|
#include "gpriority_queue.h" |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include <stdint.h> /* for uintptr_t, SIZE_MAX */ |
||||
|
#include <stdio.h> /* for printf() */ |
||||
|
#include <stdlib.h> /* for srand(), rand(), malloc(), free() */ |
||||
|
|
||||
|
static int less_comparer(const void *const ctx, const void *const a, |
||||
|
const void *const b) |
||||
|
{ |
||||
|
uintptr_t must_invert = (uintptr_t)ctx; |
||||
|
return (must_invert ? (*(int *)b < *(int *)a): (*(int *)a < *(int *)b)); |
||||
|
} |
||||
|
|
||||
|
static void item_mover(void *const dst, const void *const src) |
||||
|
{ |
||||
|
*((int *)dst) = *((int *)src); |
||||
|
} |
||||
|
|
||||
|
static void test_parent_child(const struct gheap_ctx *const ctx, |
||||
|
const size_t start_index, const size_t n) |
||||
|
{ |
||||
|
assert(start_index > 0); |
||||
|
assert(start_index <= SIZE_MAX - n); |
||||
|
|
||||
|
printf(" test_parent_child(start_index=%zu, n=%zu) ", start_index, n); |
||||
|
|
||||
|
const size_t fanout = ctx->fanout; |
||||
|
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t u = start_index + i; |
||||
|
size_t v = gheap_get_child_index(ctx, u); |
||||
|
if (v < SIZE_MAX) { |
||||
|
assert(v > u); |
||||
|
v = gheap_get_parent_index(ctx, v); |
||||
|
assert(v == u); |
||||
|
} |
||||
|
|
||||
|
v = gheap_get_parent_index(ctx, u); |
||||
|
assert(v < u); |
||||
|
v = gheap_get_child_index(ctx, v); |
||||
|
assert(v <= u && u - v < fanout); |
||||
|
} |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_is_heap(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
assert(n > 0); |
||||
|
|
||||
|
printf(" test_is_heap(n=%zu) ", n); |
||||
|
|
||||
|
/* Verify that ascending sorted array creates one-item heap. */ |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = i; |
||||
|
} |
||||
|
assert(gheap_is_heap_until(ctx, a, n) == 1); |
||||
|
assert(gheap_is_heap(ctx, a, 1)); |
||||
|
if (n > 1) { |
||||
|
assert(!gheap_is_heap(ctx, a, n)); |
||||
|
} |
||||
|
|
||||
|
/* Verify that descending sorted array creates valid heap. */ |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = n - i; |
||||
|
} |
||||
|
assert(gheap_is_heap_until(ctx, a, n) == n); |
||||
|
assert(gheap_is_heap(ctx, a, n)); |
||||
|
|
||||
|
/* Verify that array containing identical items creates valid heap. */ |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = n; |
||||
|
} |
||||
|
assert(gheap_is_heap_until(ctx, a, n) == n); |
||||
|
assert(gheap_is_heap(ctx, a, n)); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void init_array(int *const a, const size_t n) |
||||
|
{ |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a[i] = rand(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void assert_sorted(const struct gheap_ctx *const ctx, |
||||
|
const int *const base, const size_t n) |
||||
|
{ |
||||
|
const gheap_less_comparer_t less_comparer = ctx->less_comparer; |
||||
|
const void *const less_comparer_ctx = ctx->less_comparer_ctx; |
||||
|
|
||||
|
for (size_t i = 1; i < n; ++i) { |
||||
|
assert(!less_comparer(less_comparer_ctx, &base[i], &base[i - 1])); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
static void test_make_heap(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_make_heap(n=%zu) ", n); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
gheap_make_heap(ctx, a, n); |
||||
|
assert(gheap_is_heap(ctx, a, n)); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_sort_heap(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_sort_heap(n=%zu) ", n); |
||||
|
|
||||
|
/* Verify ascending sorting. */ |
||||
|
init_array(a, n); |
||||
|
gheap_make_heap(ctx, a, n); |
||||
|
gheap_sort_heap(ctx, a, n); |
||||
|
assert_sorted(ctx, a, n); |
||||
|
|
||||
|
/* Verify descending sorting. */ |
||||
|
const struct gheap_ctx ctx_desc = { |
||||
|
.fanout = ctx->fanout, |
||||
|
.page_chunks = ctx->page_chunks, |
||||
|
.item_size = ctx->item_size, |
||||
|
.less_comparer = &less_comparer, |
||||
|
.less_comparer_ctx = (void *)1, |
||||
|
.item_mover = ctx->item_mover, |
||||
|
}; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
gheap_make_heap(&ctx_desc, a, n); |
||||
|
gheap_sort_heap(&ctx_desc, a, n); |
||||
|
assert_sorted(&ctx_desc, a, n); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_push_heap(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_push_heap(n=%zu) ", n); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
gheap_push_heap(ctx, a, i + 1); |
||||
|
} |
||||
|
assert(gheap_is_heap(ctx, a, n)); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_pop_heap(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_pop_heap(n=%zu) ", n); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
|
||||
|
gheap_make_heap(ctx, a, n); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const int item = a[0]; |
||||
|
gheap_pop_heap(ctx, a, n - i); |
||||
|
assert(item == a[n - i - 1]); |
||||
|
} |
||||
|
assert_sorted(ctx, a, n); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_swap_max_item(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_swap_max_item(n=%zu) ", n); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
|
||||
|
const size_t m = n / 2; |
||||
|
if (m > 0) { |
||||
|
gheap_make_heap(ctx, a, m); |
||||
|
for (size_t i = m; i < n; ++i) { |
||||
|
const int max_item = a[0]; |
||||
|
gheap_swap_max_item(ctx, a, m, &a[i]); |
||||
|
assert(max_item == a[i]); |
||||
|
assert(gheap_is_heap(ctx, a, m)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_restore_heap_after_item_increase( |
||||
|
const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_restore_heap_after_item_increase(n=%zu) ", n); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
|
||||
|
gheap_make_heap(ctx, a, n); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t item_index = rand() % n; |
||||
|
const int old_item = a[item_index]; |
||||
|
|
||||
|
/* Don't allow integer overflow. */ |
||||
|
size_t fade = SIZE_MAX; |
||||
|
do { |
||||
|
/* Division by zero is impossible here. */ |
||||
|
a[item_index] = old_item + rand() % fade; |
||||
|
fade /= 2; |
||||
|
} while (a[item_index] < old_item); |
||||
|
gheap_restore_heap_after_item_increase(ctx, a, n, item_index); |
||||
|
assert(gheap_is_heap(ctx, a, n)); |
||||
|
} |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_restore_heap_after_item_decrease( |
||||
|
const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_restore_heap_after_item_decrease(n=%zu) ", n); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
|
||||
|
gheap_make_heap(ctx, a, n); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t item_index = rand() % n; |
||||
|
const int old_item = a[item_index]; |
||||
|
|
||||
|
/* Don't allow integer underflow. */ |
||||
|
size_t fade = SIZE_MAX; |
||||
|
do { |
||||
|
/* Division by zero is impossible here. */ |
||||
|
a[item_index] = old_item - rand() % fade; |
||||
|
fade /= 2; |
||||
|
} while (a[item_index] > old_item); |
||||
|
gheap_restore_heap_after_item_decrease(ctx, a, n, item_index); |
||||
|
assert(gheap_is_heap(ctx, a, n)); |
||||
|
} |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_remove_from_heap(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_remove_from_heap(n=%zu) ", n); |
||||
|
|
||||
|
init_array(a, n); |
||||
|
|
||||
|
gheap_make_heap(ctx, a, n); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t item_index = rand() % (n - i); |
||||
|
const int item = a[item_index]; |
||||
|
gheap_remove_from_heap(ctx, a, n - i, item_index); |
||||
|
assert(gheap_is_heap(ctx, a, n - i - 1)); |
||||
|
assert(item == a[n - i - 1]); |
||||
|
} |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static const int *min_element(const int *const a, const size_t n) |
||||
|
{ |
||||
|
assert(n > 0); |
||||
|
const int *min_ptr = a; |
||||
|
for (size_t i = 1; i < n; ++i) { |
||||
|
if (a[i] < *min_ptr) { |
||||
|
min_ptr = a + i; |
||||
|
} |
||||
|
} |
||||
|
return min_ptr; |
||||
|
} |
||||
|
|
||||
|
static void test_heapsort(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_heapsort(n=%zu) ", n); |
||||
|
|
||||
|
/* Verify ascending sorting. */ |
||||
|
init_array(a, n); |
||||
|
galgorithm_heapsort(ctx, a, n); |
||||
|
assert_sorted(ctx, a, n); |
||||
|
|
||||
|
/* Verify descending sorting. */ |
||||
|
const struct gheap_ctx ctx_desc = { |
||||
|
.fanout = ctx->fanout, |
||||
|
.page_chunks = ctx->page_chunks, |
||||
|
.item_size = ctx->item_size, |
||||
|
.less_comparer = &less_comparer, |
||||
|
.less_comparer_ctx = (void *)1, |
||||
|
.item_mover = ctx->item_mover, |
||||
|
}; |
||||
|
|
||||
|
init_array(a, n); |
||||
|
galgorithm_heapsort(&ctx_desc, a, n); |
||||
|
assert_sorted(&ctx_desc, a, n); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_partial_sort(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_partial_sort(n=%zu) ", n); |
||||
|
|
||||
|
// Check 0-items partial sort.
|
||||
|
init_array(a, n); |
||||
|
galgorithm_partial_sort(ctx, a, n, 0); |
||||
|
|
||||
|
// Check 1-item partial sort.
|
||||
|
if (n > 0) { |
||||
|
init_array(a, n); |
||||
|
galgorithm_partial_sort(ctx, a, n, 1); |
||||
|
assert(min_element(a, n) == a); |
||||
|
} |
||||
|
|
||||
|
// Check 2-items partial sort.
|
||||
|
if (n > 1) { |
||||
|
init_array(a, n); |
||||
|
galgorithm_partial_sort(ctx, a, n, 2); |
||||
|
assert_sorted(ctx, a, 2); |
||||
|
assert(min_element(a + 1, n - 1) == a + 1); |
||||
|
} |
||||
|
|
||||
|
// Check n-items partial sort.
|
||||
|
init_array(a, n); |
||||
|
galgorithm_partial_sort(ctx, a, n, n); |
||||
|
assert_sorted(ctx, a, n); |
||||
|
|
||||
|
// Check (n-1)-items partial sort.
|
||||
|
if (n > 0) { |
||||
|
init_array(a, n); |
||||
|
galgorithm_partial_sort(ctx, a, n, n - 1); |
||||
|
assert_sorted(ctx, a, n); |
||||
|
} |
||||
|
|
||||
|
// Check (n-2)-items partial sort.
|
||||
|
if (n > 2) { |
||||
|
init_array(a, n); |
||||
|
galgorithm_partial_sort(ctx, a, n, n - 2); |
||||
|
assert_sorted(ctx, a, n - 2); |
||||
|
assert(min_element(a + n - 3, 3) == a + n - 3); |
||||
|
} |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
struct nway_merge_input_ctx |
||||
|
{ |
||||
|
int *next; |
||||
|
int *end; |
||||
|
}; |
||||
|
|
||||
|
static int nway_merge_input_next(void *const ctx) |
||||
|
{ |
||||
|
struct nway_merge_input_ctx *const c = ctx; |
||||
|
assert(c->next <= c->end); |
||||
|
if (c->next < c->end) { |
||||
|
++(c->next); |
||||
|
} |
||||
|
return (c->next < c->end); |
||||
|
} |
||||
|
|
||||
|
static const void *nway_merge_input_get(const void *const ctx) |
||||
|
{ |
||||
|
const struct nway_merge_input_ctx *const c = ctx; |
||||
|
assert(c->next < c->end); |
||||
|
return c->next; |
||||
|
} |
||||
|
|
||||
|
static void nway_ctx_mover(void *const dst, const void *const src) |
||||
|
{ |
||||
|
*(struct nway_merge_input_ctx *)dst = *(struct nway_merge_input_ctx *)src; |
||||
|
} |
||||
|
|
||||
|
static const struct galgorithm_nway_merge_input_vtable |
||||
|
nway_merge_input_vtable = { |
||||
|
.next = &nway_merge_input_next, |
||||
|
.get = &nway_merge_input_get, |
||||
|
}; |
||||
|
|
||||
|
struct nway_merge_output_ctx |
||||
|
{ |
||||
|
int *next; |
||||
|
}; |
||||
|
|
||||
|
static void nway_merge_output_put(void *const ctx, const void *const data) |
||||
|
{ |
||||
|
struct nway_merge_output_ctx *const c = ctx; |
||||
|
item_mover(c->next, data); |
||||
|
++(c->next); |
||||
|
} |
||||
|
|
||||
|
static const struct galgorithm_nway_merge_output_vtable |
||||
|
nway_merge_output_vtable = { |
||||
|
.put = &nway_merge_output_put, |
||||
|
}; |
||||
|
|
||||
|
static void small_range_sorter(const void *const ctx, |
||||
|
void *const a, const size_t n) |
||||
|
{ |
||||
|
galgorithm_heapsort(ctx, a, n); |
||||
|
} |
||||
|
|
||||
|
static void test_nway_mergesort(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_nway_mergesort(n=%zu) ", n); |
||||
|
|
||||
|
int *const items_tmp_buf = malloc(sizeof(a[0]) * n); |
||||
|
|
||||
|
// Verify 2-way mergesort with small_range_size = 1.
|
||||
|
init_array(a, n); |
||||
|
galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 1, 2, |
||||
|
items_tmp_buf); |
||||
|
assert_sorted(ctx, a, n); |
||||
|
|
||||
|
// Verify 3-way mergesort with small_range_size = 2.
|
||||
|
init_array(a, n); |
||||
|
galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 2, 3, |
||||
|
items_tmp_buf); |
||||
|
assert_sorted(ctx, a, n); |
||||
|
|
||||
|
// Verify 10-way mergesort with small_range_size = 9.
|
||||
|
init_array(a, n); |
||||
|
galgorithm_nway_mergesort(ctx, a, n, &small_range_sorter, ctx, 10, 9, |
||||
|
items_tmp_buf); |
||||
|
assert_sorted(ctx, a, n); |
||||
|
|
||||
|
free(items_tmp_buf); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void test_nway_merge(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_nway_merge(n=%zu) ", n); |
||||
|
|
||||
|
int *const b = malloc(sizeof(*b) * n); |
||||
|
|
||||
|
struct galgorithm_nway_merge_input input = { |
||||
|
.vtable = &nway_merge_input_vtable, |
||||
|
.ctxs = NULL, |
||||
|
.ctxs_count = 0, |
||||
|
.ctx_size = sizeof(struct nway_merge_input_ctx), |
||||
|
.ctx_mover = &nway_ctx_mover, |
||||
|
}; |
||||
|
|
||||
|
struct nway_merge_output_ctx out_ctx; |
||||
|
|
||||
|
const struct galgorithm_nway_merge_output output = { |
||||
|
.vtable = &nway_merge_output_vtable, |
||||
|
.ctx = &out_ctx, |
||||
|
}; |
||||
|
|
||||
|
// Check 1-way merge.
|
||||
|
init_array(a, n); |
||||
|
galgorithm_heapsort(ctx, a, n); |
||||
|
|
||||
|
struct nway_merge_input_ctx one_way_input_ctxs[1] = { |
||||
|
{ |
||||
|
.next = a, |
||||
|
.end = a + n, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
input.ctxs = one_way_input_ctxs; |
||||
|
input.ctxs_count = 1; |
||||
|
out_ctx.next = b; |
||||
|
galgorithm_nway_merge(ctx, &input, &output); |
||||
|
assert_sorted(ctx, b, n); |
||||
|
|
||||
|
// Check 2-way merge.
|
||||
|
if (n > 1) { |
||||
|
init_array(a, n); |
||||
|
galgorithm_heapsort(ctx, a, n / 2); |
||||
|
galgorithm_heapsort(ctx, a + n / 2, n - n / 2); |
||||
|
|
||||
|
struct nway_merge_input_ctx two_way_input_ctxs[2] = { |
||||
|
{ |
||||
|
.next = a, |
||||
|
.end = a + n / 2, |
||||
|
}, |
||||
|
{ |
||||
|
.next = a + n / 2, |
||||
|
.end = a + n, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
input.ctxs = two_way_input_ctxs; |
||||
|
input.ctxs_count = 2; |
||||
|
out_ctx.next = b; |
||||
|
galgorithm_nway_merge(ctx, &input, &output); |
||||
|
assert_sorted(ctx, b, n); |
||||
|
} |
||||
|
|
||||
|
// Check n-way merge with n sorted lists each containing exactly one item.
|
||||
|
init_array(a, n); |
||||
|
struct nway_merge_input_ctx *const nway_merge_input_ctxs = |
||||
|
malloc(sizeof(nway_merge_input_ctxs[0]) * n); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
struct nway_merge_input_ctx *const input_ctx = &nway_merge_input_ctxs[i]; |
||||
|
input_ctx->next = a + i; |
||||
|
input_ctx->end = a + (i + 1); |
||||
|
} |
||||
|
|
||||
|
input.ctxs = nway_merge_input_ctxs; |
||||
|
input.ctxs_count = n; |
||||
|
out_ctx.next = b; |
||||
|
galgorithm_nway_merge(ctx, &input, &output); |
||||
|
assert_sorted(ctx, b, n); |
||||
|
|
||||
|
free(nway_merge_input_ctxs); |
||||
|
|
||||
|
free(b); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void item_deleter(void *item) |
||||
|
{ |
||||
|
/* do nothing */ |
||||
|
(void)item; |
||||
|
} |
||||
|
|
||||
|
static void test_priority_queue(const struct gheap_ctx *const ctx, |
||||
|
const size_t n, int *const a) |
||||
|
{ |
||||
|
printf(" test_priority_queue(n=%zu) ", n); |
||||
|
|
||||
|
// Verify emptry priority queue.
|
||||
|
struct gpriority_queue *const q_empty = gpriority_queue_create(ctx, |
||||
|
&item_deleter); |
||||
|
assert(gpriority_queue_empty(q_empty)); |
||||
|
assert(gpriority_queue_size(q_empty) == 0); |
||||
|
|
||||
|
// Verify non-empty priority queue.
|
||||
|
init_array(a, n); |
||||
|
struct gpriority_queue *const q = gpriority_queue_create_from_array(ctx, |
||||
|
&item_deleter, a, n); |
||||
|
assert(!gpriority_queue_empty(q)); |
||||
|
assert(gpriority_queue_size(q) == n); |
||||
|
|
||||
|
// Pop all items from the priority queue.
|
||||
|
int max_item = *(int *)gpriority_queue_top(q); |
||||
|
for (size_t i = 1; i < n; ++i) { |
||||
|
gpriority_queue_pop(q); |
||||
|
assert(gpriority_queue_size(q) == n - i); |
||||
|
assert(*(int *)gpriority_queue_top(q) <= max_item); |
||||
|
max_item = *(int *)gpriority_queue_top(q); |
||||
|
} |
||||
|
assert(*(int *)gpriority_queue_top(q) <= max_item); |
||||
|
gpriority_queue_pop(q); |
||||
|
assert(gpriority_queue_empty(q)); |
||||
|
|
||||
|
// Push items to priority queue.
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const int tmp = rand(); |
||||
|
gpriority_queue_push(q, &tmp); |
||||
|
assert(gpriority_queue_size(q) == i + 1); |
||||
|
} |
||||
|
|
||||
|
// Interleave pushing and popping items in priority queue.
|
||||
|
max_item = *(int *)gpriority_queue_top(q); |
||||
|
for (size_t i = 1; i < n; ++i) { |
||||
|
gpriority_queue_pop(q); |
||||
|
assert(*(int*)gpriority_queue_top(q) <= max_item); |
||||
|
const int tmp = rand(); |
||||
|
if (tmp > max_item) { |
||||
|
max_item = tmp; |
||||
|
} |
||||
|
gpriority_queue_push(q, &tmp); |
||||
|
} |
||||
|
assert(gpriority_queue_size(q) == n); |
||||
|
|
||||
|
printf("OK\n"); |
||||
|
} |
||||
|
|
||||
|
static void run_all(const struct gheap_ctx *const ctx, |
||||
|
void (*func)(const struct gheap_ctx *, size_t, int *)) |
||||
|
{ |
||||
|
int *const a = malloc(1001 * sizeof(*a)); |
||||
|
|
||||
|
for (size_t i = 1; i < 12; ++i) { |
||||
|
func(ctx, i, a); |
||||
|
} |
||||
|
func(ctx, 1001, a); |
||||
|
|
||||
|
free(a); |
||||
|
} |
||||
|
|
||||
|
static void test_all(const size_t fanout, const size_t page_chunks) |
||||
|
{ |
||||
|
printf(" test_all(fanout=%zu, page_chunks=%zu) start\n", |
||||
|
fanout, page_chunks); |
||||
|
|
||||
|
const struct gheap_ctx ctx_v = { |
||||
|
.fanout = fanout, |
||||
|
.page_chunks = page_chunks, |
||||
|
.item_size = sizeof(int), |
||||
|
.less_comparer = &less_comparer, |
||||
|
.less_comparer_ctx = (void *)0, |
||||
|
.item_mover = &item_mover, |
||||
|
}; |
||||
|
const struct gheap_ctx *const ctx = &ctx_v; |
||||
|
|
||||
|
/* Verify parent-child calculations for indexes close to zero and
|
||||
|
* indexes close to SIZE_MAX. |
||||
|
*/ |
||||
|
static const size_t n = 1000000; |
||||
|
test_parent_child(ctx, 1, n); |
||||
|
test_parent_child(ctx, SIZE_MAX - n, n); |
||||
|
|
||||
|
run_all(ctx, test_is_heap); |
||||
|
run_all(ctx, test_make_heap); |
||||
|
run_all(ctx, test_sort_heap); |
||||
|
run_all(ctx, test_push_heap); |
||||
|
run_all(ctx, test_pop_heap); |
||||
|
run_all(ctx, test_swap_max_item); |
||||
|
run_all(ctx, test_restore_heap_after_item_increase); |
||||
|
run_all(ctx, test_restore_heap_after_item_decrease); |
||||
|
run_all(ctx, test_remove_from_heap); |
||||
|
run_all(ctx, test_heapsort); |
||||
|
run_all(ctx, test_partial_sort); |
||||
|
run_all(ctx, test_nway_merge); |
||||
|
run_all(ctx, test_nway_mergesort); |
||||
|
run_all(ctx, test_priority_queue); |
||||
|
|
||||
|
printf(" test_all(fanout=%zu, page_chunks=%zu) OK\n", fanout, page_chunks); |
||||
|
} |
||||
|
|
||||
|
static void main_test(void) |
||||
|
{ |
||||
|
printf("main_test() start\n"); |
||||
|
|
||||
|
test_all(1, 1); |
||||
|
test_all(2, 1); |
||||
|
test_all(3, 1); |
||||
|
test_all(4, 1); |
||||
|
test_all(101, 1); |
||||
|
|
||||
|
test_all(1, 2); |
||||
|
test_all(2, 2); |
||||
|
test_all(3, 2); |
||||
|
test_all(4, 2); |
||||
|
test_all(101, 2); |
||||
|
|
||||
|
test_all(1, 3); |
||||
|
test_all(2, 3); |
||||
|
test_all(3, 3); |
||||
|
test_all(4, 3); |
||||
|
test_all(101, 3); |
||||
|
|
||||
|
test_all(1, 4); |
||||
|
test_all(2, 4); |
||||
|
test_all(3, 4); |
||||
|
test_all(4, 4); |
||||
|
test_all(101, 4); |
||||
|
|
||||
|
test_all(1, 101); |
||||
|
test_all(2, 101); |
||||
|
test_all(3, 101); |
||||
|
test_all(4, 101); |
||||
|
test_all(101, 101); |
||||
|
|
||||
|
printf("main_test() OK\n"); |
||||
|
} |
||||
|
|
||||
|
int main(void) |
||||
|
{ |
||||
|
srand(0); |
||||
|
main_test(); |
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,627 @@ |
|||||
|
// Tests for C++03 and C++11 gheap, galgorithm and gpriority_queue.
|
||||
|
//
|
||||
|
// Pass -DGHEAP_CPP11 to compiler for gheap_cpp11.hpp tests,
|
||||
|
// otherwise gheap_cpp03.hpp will be tested.
|
||||
|
|
||||
|
#include "galgorithm.hpp" |
||||
|
#include "gheap.hpp" |
||||
|
#include "gpriority_queue.hpp" |
||||
|
|
||||
|
#include <algorithm> // for min_element() |
||||
|
#include <cassert> |
||||
|
#include <cstdlib> // for srand(), rand() |
||||
|
#include <deque> |
||||
|
#include <iostream> // for cout |
||||
|
#include <iterator> // for back_inserter |
||||
|
#include <vector> |
||||
|
#include <utility> // for pair |
||||
|
|
||||
|
#ifndef GHEAP_CPP11 |
||||
|
# include <algorithm> // for swap()
|
||||
|
#endif |
||||
|
|
||||
|
using namespace std; |
||||
|
|
||||
|
namespace { |
||||
|
|
||||
|
template <class Heap> |
||||
|
void test_parent_child(const size_t start_index, const size_t n) |
||||
|
{ |
||||
|
assert(start_index > 0); |
||||
|
assert(start_index <= SIZE_MAX - n); |
||||
|
|
||||
|
cout << " test_parent_child(start_index=" << start_index << ", n=" << n << |
||||
|
") "; |
||||
|
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t u = start_index + i; |
||||
|
size_t v = Heap::get_child_index(u); |
||||
|
if (v < SIZE_MAX) { |
||||
|
assert(v > u); |
||||
|
v = Heap::get_parent_index(v); |
||||
|
assert(v == u); |
||||
|
} |
||||
|
|
||||
|
v = Heap::get_parent_index(u); |
||||
|
assert(v < u); |
||||
|
v = Heap::get_child_index(v); |
||||
|
assert(v <= u && u - v < Heap::FANOUT); |
||||
|
} |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_is_heap(const size_t n) |
||||
|
{ |
||||
|
assert(n > 0); |
||||
|
|
||||
|
cout << " test_is_heap(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
|
||||
|
// Verify that ascending sorted array creates one-item heap.
|
||||
|
a.clear(); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a.push_back(i); |
||||
|
} |
||||
|
assert(Heap::is_heap_until(a.begin(), a.end()) == a.begin() + 1); |
||||
|
assert(Heap::is_heap(a.begin(), a.begin() + 1)); |
||||
|
if (n > 1) { |
||||
|
assert(!Heap::is_heap(a.begin(), a.end())); |
||||
|
} |
||||
|
|
||||
|
// Verify that descending sorted array creates valid heap.
|
||||
|
a.clear(); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a.push_back(n - i); |
||||
|
} |
||||
|
assert(Heap::is_heap_until(a.begin(), a.end()) == a.end()); |
||||
|
assert(Heap::is_heap(a.begin(), a.end())); |
||||
|
|
||||
|
// Verify that array containing identical items creates valid heap.
|
||||
|
a.clear(); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a.push_back(n); |
||||
|
} |
||||
|
assert(Heap::is_heap_until(a.begin(), a.end()) == a.end()); |
||||
|
assert(Heap::is_heap(a.begin(), a.end())); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class IntContainer> |
||||
|
void init_array(IntContainer &a, const size_t n) |
||||
|
{ |
||||
|
a.clear(); |
||||
|
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
a.push_back(rand()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
void assert_sorted_asc(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
|
||||
|
const size_t size = last - first; |
||||
|
for (size_t i = 1; i < size; ++i) { |
||||
|
assert(first[i] >= first[i - 1]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
template <class RandomAccessIterator> |
||||
|
void assert_sorted_desc(const RandomAccessIterator &first, |
||||
|
const RandomAccessIterator &last) |
||||
|
{ |
||||
|
assert(last > first); |
||||
|
|
||||
|
const size_t size = last - first; |
||||
|
for (size_t i = 1; i < size; ++i) { |
||||
|
assert(first[i] <= first[i - 1]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
bool less_comparer_desc(const int &a, const int &b) |
||||
|
{ |
||||
|
return (b < a); |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_make_heap(const size_t n) |
||||
|
{ |
||||
|
cout << " test_make_heap(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
assert(Heap::is_heap(a.begin(), a.end())); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_sort_heap(const size_t n) |
||||
|
{ |
||||
|
cout << " test_sort_heap(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
|
||||
|
// Test ascending sorting
|
||||
|
init_array(a, n); |
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
Heap::sort_heap(a.begin(), a.end()); |
||||
|
assert_sorted_asc(a.begin(), a.end()); |
||||
|
|
||||
|
// Test descending sorting
|
||||
|
init_array(a, n); |
||||
|
Heap::make_heap(a.begin(), a.end(), less_comparer_desc); |
||||
|
Heap::sort_heap(a.begin(), a.end(), less_comparer_desc); |
||||
|
assert_sorted_desc(a.begin(), a.end()); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_push_heap(const size_t n) |
||||
|
{ |
||||
|
cout << " test_push_heap(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
Heap::push_heap(a.begin(), a.begin() + i + 1); |
||||
|
} |
||||
|
assert(Heap::is_heap(a.begin(), a.end())); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_pop_heap(const size_t n) |
||||
|
{ |
||||
|
cout << " test_pop_heap(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
|
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const int item = a[0]; |
||||
|
Heap::pop_heap(a.begin(), a.end() - i); |
||||
|
assert(item == *(a.end() - i - 1)); |
||||
|
} |
||||
|
assert_sorted_asc(a.begin(), a.end()); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_swap_max_item(const size_t n) |
||||
|
{ |
||||
|
typedef typename IntContainer::iterator iterator; |
||||
|
|
||||
|
cout << " test_swap_max_item(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
|
||||
|
const size_t m = n / 2; |
||||
|
|
||||
|
if (m > 0) { |
||||
|
Heap::make_heap(a.begin(), a.begin() + m); |
||||
|
for (size_t i = m; i < n; ++i) { |
||||
|
const int max_item = a[0]; |
||||
|
Heap::swap_max_item(a.begin(), a.begin() + m, a[i]); |
||||
|
assert(max_item == a[i]); |
||||
|
assert(Heap::is_heap(a.begin(), a.begin() + m)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_restore_heap_after_item_increase(const size_t n) |
||||
|
{ |
||||
|
cout << " test_restore_heap_after_item_increase(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
|
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t item_index = rand() % n; |
||||
|
const int old_item = a[item_index]; |
||||
|
|
||||
|
// Don't allow integer overflow.
|
||||
|
size_t fade = SIZE_MAX; |
||||
|
do { |
||||
|
// Division by zero is impossible here.
|
||||
|
a[item_index] = old_item + rand() % fade; |
||||
|
fade /= 2; |
||||
|
} while (a[item_index] < old_item); |
||||
|
Heap::restore_heap_after_item_increase(a.begin(), a.begin() + item_index); |
||||
|
assert(Heap::is_heap(a.begin(), a.end())); |
||||
|
} |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_restore_heap_after_item_decrease(const size_t n) |
||||
|
{ |
||||
|
cout << " test_restore_heap_after_item_decrease(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
|
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t item_index = rand() % n; |
||||
|
const int old_item = a[item_index]; |
||||
|
|
||||
|
// Don't allow integer underflow.
|
||||
|
size_t fade = SIZE_MAX; |
||||
|
do { |
||||
|
// Division by zero is impossible here.
|
||||
|
a[item_index] = old_item - rand() % fade; |
||||
|
fade /= 2; |
||||
|
} while (a[item_index] > old_item); |
||||
|
Heap::restore_heap_after_item_decrease(a.begin(), a.begin() + item_index, |
||||
|
a.end()); |
||||
|
assert(Heap::is_heap(a.begin(), a.end())); |
||||
|
} |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_remove_from_heap(const size_t n) |
||||
|
{ |
||||
|
cout << " test_remove_from_heap(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
|
||||
|
Heap::make_heap(a.begin(), a.end()); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
const size_t item_index = rand() % (n - i); |
||||
|
const int item = a[item_index]; |
||||
|
Heap::remove_from_heap(a.begin(), a.begin() + item_index, a.end() - i); |
||||
|
assert(Heap::is_heap(a.begin(), a.end() - i - 1)); |
||||
|
assert(item == *(a.end() - i - 1)); |
||||
|
} |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_heapsort(const size_t n) |
||||
|
{ |
||||
|
typedef galgorithm<Heap> algorithm; |
||||
|
|
||||
|
cout << " test_heapsort(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
|
||||
|
// Verify ascending sorting with default less_comparer.
|
||||
|
init_array(a, n); |
||||
|
algorithm::heapsort(a.begin(), a.end()); |
||||
|
assert_sorted_asc(a.begin(), a.end()); |
||||
|
|
||||
|
// Verify descending sorting with custom less_comparer.
|
||||
|
init_array(a, n); |
||||
|
algorithm::heapsort(a.begin(), a.end(), less_comparer_desc); |
||||
|
assert_sorted_desc(a.begin(), a.end()); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_partial_sort(const size_t n) |
||||
|
{ |
||||
|
typedef galgorithm<Heap> algorithm; |
||||
|
typedef typename IntContainer::iterator iterator; |
||||
|
|
||||
|
cout << " test_partial_sort(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
|
||||
|
// Check 0-items partial sort.
|
||||
|
init_array(a, n); |
||||
|
algorithm::partial_sort(a.begin(), a.begin(), a.end()); |
||||
|
|
||||
|
// Check 1-item partial sort.
|
||||
|
if (n > 0) { |
||||
|
init_array(a, n); |
||||
|
algorithm::partial_sort(a.begin(), a.begin() + 1, a.end()); |
||||
|
assert(min_element(a.begin(), a.end()) == a.begin()); |
||||
|
} |
||||
|
|
||||
|
// Check 2-items partial sort.
|
||||
|
if (n > 1) { |
||||
|
init_array(a, n); |
||||
|
algorithm::partial_sort(a.begin(), a.begin() + 2, a.end()); |
||||
|
assert_sorted_asc(a.begin(), a.begin() + 2); |
||||
|
assert(min_element(a.begin() + 1, a.end()) == a.begin() + 1); |
||||
|
} |
||||
|
|
||||
|
// Check n-items partial sort.
|
||||
|
init_array(a, n); |
||||
|
algorithm::partial_sort(a.begin(), a.end(), a.end()); |
||||
|
assert_sorted_asc(a.begin(), a.end()); |
||||
|
|
||||
|
// Check (n-1)-items partial sort.
|
||||
|
if (n > 0) { |
||||
|
init_array(a, n); |
||||
|
algorithm::partial_sort(a.begin(), a.end() - 1, a.end()); |
||||
|
assert_sorted_asc(a.begin(), a.end()); |
||||
|
} |
||||
|
|
||||
|
// Check (n-2)-items partial sort.
|
||||
|
if (n > 2) { |
||||
|
init_array(a, n); |
||||
|
algorithm::partial_sort(a.begin(), a.end() - 2, a.end()); |
||||
|
assert_sorted_asc(a.begin(), a.end() - 2); |
||||
|
assert(min_element(a.end() - 3, a.end()) == a.end() - 3); |
||||
|
} |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_nway_merge(const size_t n) |
||||
|
{ |
||||
|
typedef galgorithm<Heap> algorithm; |
||||
|
typedef typename IntContainer::iterator iterator; |
||||
|
|
||||
|
cout << " test_nway_merge(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a, b; |
||||
|
vector<pair<iterator, iterator> > input_ranges; |
||||
|
|
||||
|
// Check 1-way merge.
|
||||
|
init_array(a, n); |
||||
|
b.clear(); |
||||
|
input_ranges.clear(); |
||||
|
algorithm::heapsort(a.begin(), a.end()); |
||||
|
input_ranges.push_back(pair<iterator, iterator>(a.begin(), a.end())); |
||||
|
algorithm::nway_merge(input_ranges.begin(), input_ranges.end(), |
||||
|
back_inserter(b)); |
||||
|
assert_sorted_asc(b.begin(), b.end()); |
||||
|
|
||||
|
// Check 2-way merge.
|
||||
|
if (n > 1) { |
||||
|
init_array(a, n); |
||||
|
b.clear(); |
||||
|
input_ranges.clear(); |
||||
|
const iterator middle = a.begin() + n / 2; |
||||
|
algorithm::heapsort(a.begin(), middle); |
||||
|
algorithm::heapsort(middle, a.end()); |
||||
|
input_ranges.push_back(pair<iterator, iterator>(a.begin(), middle)); |
||||
|
input_ranges.push_back(pair<iterator, iterator>(middle, a.end())); |
||||
|
algorithm::nway_merge(input_ranges.begin(), input_ranges.end(), |
||||
|
back_inserter(b)); |
||||
|
assert_sorted_asc(b.begin(), b.end()); |
||||
|
} |
||||
|
|
||||
|
// Check n-way merge with n sorted lists each containing exactly one item.
|
||||
|
init_array(a, n); |
||||
|
b.clear(); |
||||
|
input_ranges.clear(); |
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
input_ranges.push_back(pair<iterator, iterator>(a.begin() + i, |
||||
|
a.begin() + (i + 1))); |
||||
|
} |
||||
|
algorithm::nway_merge(input_ranges.begin(), input_ranges.end(), |
||||
|
back_inserter(b)); |
||||
|
assert_sorted_asc(b.begin(), b.end()); |
||||
|
|
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class T> |
||||
|
void small_range_sorter(T *const first, T *const last, |
||||
|
bool (&less_comparer)(const T &, const T &)) |
||||
|
{ |
||||
|
galgorithm<gheap<2, 1> >::heapsort(first, last, less_comparer); |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_nway_mergesort(const size_t n) |
||||
|
{ |
||||
|
typedef galgorithm<Heap> algorithm; |
||||
|
typedef typename IntContainer::value_type value_type; |
||||
|
|
||||
|
cout << " test_nway_mergesort(n=" << n << ") "; |
||||
|
|
||||
|
IntContainer a; |
||||
|
|
||||
|
// Verify n-way mergesort with default settings.
|
||||
|
init_array(a, n); |
||||
|
algorithm::nway_mergesort(a.begin(), a.end()); |
||||
|
assert_sorted_asc(a.begin(), a.end()); |
||||
|
|
||||
|
// Verify n-way mergesort with custom less_comparer.
|
||||
|
init_array(a, n); |
||||
|
algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc); |
||||
|
assert_sorted_desc(a.begin(), a.end()); |
||||
|
|
||||
|
// Verify n-way mergesort with custom small_range_sorter.
|
||||
|
init_array(a, n); |
||||
|
algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc, |
||||
|
small_range_sorter<value_type>); |
||||
|
assert_sorted_desc(a.begin(), a.end()); |
||||
|
|
||||
|
// Verify n-way mergesort with custom small_range_size.
|
||||
|
init_array(a, n); |
||||
|
algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc, |
||||
|
small_range_sorter<value_type>, 1); |
||||
|
assert_sorted_desc(a.begin(), a.end()); |
||||
|
|
||||
|
// Verify n-way mergesort with custom subranges_count.
|
||||
|
init_array(a, n); |
||||
|
algorithm::nway_mergesort(a.begin(), a.end(), less_comparer_desc, |
||||
|
small_range_sorter<value_type>, 2, 3); |
||||
|
assert_sorted_desc(a.begin(), a.end()); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Heap, class IntContainer> |
||||
|
void test_priority_queue(const size_t n) |
||||
|
{ |
||||
|
typedef typename IntContainer::value_type value_type; |
||||
|
typedef gpriority_queue<Heap, value_type, IntContainer> priority_queue; |
||||
|
|
||||
|
cout << " test_priority_queue(n=" << n << ") "; |
||||
|
|
||||
|
// Verify default constructor.
|
||||
|
priority_queue q_empty; |
||||
|
assert(q_empty.empty()); |
||||
|
assert(q_empty.size() == 0); |
||||
|
|
||||
|
// Verify non-empty priority queue.
|
||||
|
IntContainer a; |
||||
|
init_array(a, n); |
||||
|
priority_queue q(a.begin(), a.end()); |
||||
|
assert(!q.empty()); |
||||
|
assert(q.size() == n); |
||||
|
|
||||
|
// Verify swap().
|
||||
|
q.swap(q_empty); |
||||
|
assert(q.empty()); |
||||
|
assert(!q_empty.empty()); |
||||
|
assert(q_empty.size() == n); |
||||
|
swap(q, q_empty); |
||||
|
assert(!q.empty()); |
||||
|
assert(q.size() == n); |
||||
|
assert(q_empty.empty()); |
||||
|
|
||||
|
// Pop all items from the priority queue.
|
||||
|
int max_item = q.top(); |
||||
|
for (size_t i = 1; i < n; ++i) { |
||||
|
q.pop(); |
||||
|
assert(q.size() == n - i); |
||||
|
assert(q.top() <= max_item); |
||||
|
max_item = q.top(); |
||||
|
} |
||||
|
assert(q.top() <= max_item); |
||||
|
q.pop(); |
||||
|
assert(q.empty()); |
||||
|
|
||||
|
// Push items to priority queue.
|
||||
|
for (size_t i = 0; i < n; ++i) { |
||||
|
q.push(rand()); |
||||
|
assert(q.size() == i + 1); |
||||
|
} |
||||
|
|
||||
|
// Interleave pushing and popping items in priority queue.
|
||||
|
max_item = q.top(); |
||||
|
for (size_t i = 1; i < n; ++i) { |
||||
|
q.pop(); |
||||
|
assert(q.top() <= max_item); |
||||
|
const int tmp = rand(); |
||||
|
if (tmp > max_item) { |
||||
|
max_item = tmp; |
||||
|
} |
||||
|
q.push(tmp); |
||||
|
} |
||||
|
assert(q.size() == n); |
||||
|
|
||||
|
cout << "OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class Func> |
||||
|
void test_func(const Func &func) |
||||
|
{ |
||||
|
for (size_t i = 1; i < 12; ++i) { |
||||
|
func(i); |
||||
|
} |
||||
|
func(1001); |
||||
|
} |
||||
|
|
||||
|
template <size_t Fanout, size_t PageChunks, class IntContainer> |
||||
|
void test_all() |
||||
|
{ |
||||
|
cout << " test_all(Fanout=" << Fanout << ", PageChunks=" << PageChunks << |
||||
|
") start" << endl; |
||||
|
|
||||
|
typedef gheap<Fanout, PageChunks> heap; |
||||
|
|
||||
|
// Verify parent-child calculations for indexes close to zero and
|
||||
|
// indexes close to SIZE_MAX.
|
||||
|
static const size_t n = 1000000; |
||||
|
test_parent_child<heap>(1, n); |
||||
|
test_parent_child<heap>(SIZE_MAX - n, n); |
||||
|
|
||||
|
test_func(test_is_heap<heap, IntContainer>); |
||||
|
test_func(test_make_heap<heap, IntContainer>); |
||||
|
test_func(test_sort_heap<heap, IntContainer>); |
||||
|
test_func(test_push_heap<heap, IntContainer>); |
||||
|
test_func(test_pop_heap<heap, IntContainer>); |
||||
|
test_func(test_swap_max_item<heap, IntContainer>); |
||||
|
test_func(test_restore_heap_after_item_increase<heap, IntContainer>); |
||||
|
test_func(test_restore_heap_after_item_decrease<heap, IntContainer>); |
||||
|
test_func(test_remove_from_heap<heap, IntContainer>); |
||||
|
test_func(test_heapsort<heap, IntContainer>); |
||||
|
test_func(test_partial_sort<heap, IntContainer>); |
||||
|
test_func(test_nway_merge<heap, IntContainer>); |
||||
|
test_func(test_nway_mergesort<heap, IntContainer>); |
||||
|
test_func(test_priority_queue<heap, IntContainer>); |
||||
|
|
||||
|
cout << " test_all(Fanout=" << Fanout << ", PageChunks=" << PageChunks << |
||||
|
") OK" << endl; |
||||
|
} |
||||
|
|
||||
|
template <class IntContainer> |
||||
|
void main_test(const char *const container_name) |
||||
|
{ |
||||
|
cout << "main_test(" << container_name << ") start" << endl; |
||||
|
|
||||
|
test_all<1, 1, IntContainer>(); |
||||
|
test_all<2, 1, IntContainer>(); |
||||
|
test_all<3, 1, IntContainer>(); |
||||
|
test_all<4, 1, IntContainer>(); |
||||
|
test_all<101, 1, IntContainer>(); |
||||
|
|
||||
|
test_all<1, 2, IntContainer>(); |
||||
|
test_all<2, 2, IntContainer>(); |
||||
|
test_all<3, 2, IntContainer>(); |
||||
|
test_all<4, 2, IntContainer>(); |
||||
|
test_all<101, 2, IntContainer>(); |
||||
|
|
||||
|
test_all<1, 3, IntContainer>(); |
||||
|
test_all<2, 3, IntContainer>(); |
||||
|
test_all<3, 3, IntContainer>(); |
||||
|
test_all<4, 3, IntContainer>(); |
||||
|
test_all<101, 3, IntContainer>(); |
||||
|
|
||||
|
test_all<1, 4, IntContainer>(); |
||||
|
test_all<2, 4, IntContainer>(); |
||||
|
test_all<3, 4, IntContainer>(); |
||||
|
test_all<4, 4, IntContainer>(); |
||||
|
test_all<101, 4, IntContainer>(); |
||||
|
|
||||
|
test_all<1, 101, IntContainer>(); |
||||
|
test_all<2, 101, IntContainer>(); |
||||
|
test_all<3, 101, IntContainer>(); |
||||
|
test_all<4, 101, IntContainer>(); |
||||
|
test_all<101, 101, IntContainer>(); |
||||
|
|
||||
|
cout << "main_test(" << container_name << ") OK" << endl; |
||||
|
} |
||||
|
|
||||
|
} // End of anonymous namespace.
|
||||
|
|
||||
|
int main() |
||||
|
{ |
||||
|
srand(0); |
||||
|
main_test<vector<int> >("vector"); |
||||
|
main_test<deque<int> >("deque"); |
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
language: c |
||||
|
sudo: false |
||||
|
script: |
||||
|
- make test |
@ -0,0 +1,20 @@ |
|||||
|
Copyright (c) 2010 Serge A. Zaitsev |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
of this software and associated documentation files (the "Software"), to deal |
||||
|
in the Software without restriction, including without limitation the rights |
||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
|
copies of the Software, and to permit persons to whom the Software is |
||||
|
furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
||||
|
THE SOFTWARE. |
||||
|
|
@ -0,0 +1,41 @@ |
|||||
|
# You can put your build options here
|
||||
|
-include config.mk |
||||
|
|
||||
|
all: libjsmn.a |
||||
|
|
||||
|
libjsmn.a: jsmn.o |
||||
|
$(AR) rc $@ $^ |
||||
|
|
||||
|
%.o: %.c jsmn.h |
||||
|
$(CC) -c $(CFLAGS) $< -o $@ |
||||
|
|
||||
|
test: test_default test_strict test_links test_strict_links |
||||
|
test_default: test/tests.c |
||||
|
$(CC) $(CFLAGS) $(LDFLAGS) $< -o test/$@ |
||||
|
./test/$@ |
||||
|
test_strict: test/tests.c |
||||
|
$(CC) -DJSMN_STRICT=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ |
||||
|
./test/$@ |
||||
|
test_links: test/tests.c |
||||
|
$(CC) -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ |
||||
|
./test/$@ |
||||
|
test_strict_links: test/tests.c |
||||
|
$(CC) -DJSMN_STRICT=1 -DJSMN_PARENT_LINKS=1 $(CFLAGS) $(LDFLAGS) $< -o test/$@ |
||||
|
./test/$@ |
||||
|
|
||||
|
jsmn_test.o: jsmn_test.c libjsmn.a |
||||
|
|
||||
|
simple_example: example/simple.o libjsmn.a |
||||
|
$(CC) $(LDFLAGS) $^ -o $@ |
||||
|
|
||||
|
jsondump: example/jsondump.o libjsmn.a |
||||
|
$(CC) $(LDFLAGS) $^ -o $@ |
||||
|
|
||||
|
clean: |
||||
|
rm -f *.o example/*.o |
||||
|
rm -f *.a *.so |
||||
|
rm -f simple_example |
||||
|
rm -f jsondump |
||||
|
|
||||
|
.PHONY: all clean test |
||||
|
|
@ -0,0 +1,168 @@ |
|||||
|
JSMN |
||||
|
==== |
||||
|
|
||||
|
[![Build Status](https://travis-ci.org/zserge/jsmn.svg?branch=master)](https://travis-ci.org/zserge/jsmn) |
||||
|
|
||||
|
jsmn (pronounced like 'jasmine') is a minimalistic JSON parser in C. It can be |
||||
|
easily integrated into resource-limited or embedded projects. |
||||
|
|
||||
|
You can find more information about JSON format at [json.org][1] |
||||
|
|
||||
|
Library sources are available at https://github.com/zserge/jsmn |
||||
|
|
||||
|
The web page with some information about jsmn can be found at |
||||
|
[http://zserge.com/jsmn.html][2] |
||||
|
|
||||
|
Philosophy |
||||
|
---------- |
||||
|
|
||||
|
Most JSON parsers offer you a bunch of functions to load JSON data, parse it |
||||
|
and extract any value by its name. jsmn proves that checking the correctness of |
||||
|
every JSON packet or allocating temporary objects to store parsed JSON fields |
||||
|
often is an overkill. |
||||
|
|
||||
|
JSON format itself is extremely simple, so why should we complicate it? |
||||
|
|
||||
|
jsmn is designed to be **robust** (it should work fine even with erroneous |
||||
|
data), **fast** (it should parse data on the fly), **portable** (no superfluous |
||||
|
dependencies or non-standard C extensions). And of course, **simplicity** is a |
||||
|
key feature - simple code style, simple algorithm, simple integration into |
||||
|
other projects. |
||||
|
|
||||
|
Features |
||||
|
-------- |
||||
|
|
||||
|
* compatible with C89 |
||||
|
* no dependencies (even libc!) |
||||
|
* highly portable (tested on x86/amd64, ARM, AVR) |
||||
|
* about 200 lines of code |
||||
|
* extremely small code footprint |
||||
|
* API contains only 2 functions |
||||
|
* no dynamic memory allocation |
||||
|
* incremental single-pass parsing |
||||
|
* library code is covered with unit-tests |
||||
|
|
||||
|
Design |
||||
|
------ |
||||
|
|
||||
|
The rudimentary jsmn object is a **token**. Let's consider a JSON string: |
||||
|
|
||||
|
'{ "name" : "Jack", "age" : 27 }' |
||||
|
|
||||
|
It holds the following tokens: |
||||
|
|
||||
|
* Object: `{ "name" : "Jack", "age" : 27}` (the whole object) |
||||
|
* Strings: `"name"`, `"Jack"`, `"age"` (keys and some values) |
||||
|
* Number: `27` |
||||
|
|
||||
|
In jsmn, tokens do not hold any data, but point to token boundaries in JSON |
||||
|
string instead. In the example above jsmn will create tokens like: Object |
||||
|
[0..31], String [3..7], String [12..16], String [20..23], Number [27..29]. |
||||
|
|
||||
|
Every jsmn token has a type, which indicates the type of corresponding JSON |
||||
|
token. jsmn supports the following token types: |
||||
|
|
||||
|
* Object - a container of key-value pairs, e.g.: |
||||
|
`{ "foo":"bar", "x":0.3 }` |
||||
|
* Array - a sequence of values, e.g.: |
||||
|
`[ 1, 2, 3 ]` |
||||
|
* String - a quoted sequence of chars, e.g.: `"foo"` |
||||
|
* Primitive - a number, a boolean (`true`, `false`) or `null` |
||||
|
|
||||
|
Besides start/end positions, jsmn tokens for complex types (like arrays |
||||
|
or objects) also contain a number of child items, so you can easily follow |
||||
|
object hierarchy. |
||||
|
|
||||
|
This approach provides enough information for parsing any JSON data and makes |
||||
|
it possible to use zero-copy techniques. |
||||
|
|
||||
|
Install |
||||
|
------- |
||||
|
|
||||
|
To clone the repository you should have Git installed. Just run: |
||||
|
|
||||
|
$ git clone https://github.com/zserge/jsmn |
||||
|
|
||||
|
Repository layout is simple: jsmn.c and jsmn.h are library files, tests are in |
||||
|
the jsmn\_test.c, you will also find README, LICENSE and Makefile files inside. |
||||
|
|
||||
|
To build the library, run `make`. It is also recommended to run `make test`. |
||||
|
Let me know, if some tests fail. |
||||
|
|
||||
|
If build was successful, you should get a `libjsmn.a` library. |
||||
|
The header file you should include is called `"jsmn.h"`. |
||||
|
|
||||
|
API |
||||
|
--- |
||||
|
|
||||
|
Token types are described by `jsmntype_t`: |
||||
|
|
||||
|
typedef enum { |
||||
|
JSMN_UNDEFINED = 0, |
||||
|
JSMN_OBJECT = 1, |
||||
|
JSMN_ARRAY = 2, |
||||
|
JSMN_STRING = 3, |
||||
|
JSMN_PRIMITIVE = 4 |
||||
|
} jsmntype_t; |
||||
|
|
||||
|
**Note:** Unlike JSON data types, primitive tokens are not divided into |
||||
|
numbers, booleans and null, because one can easily tell the type using the |
||||
|
first character: |
||||
|
|
||||
|
* <code>'t', 'f'</code> - boolean |
||||
|
* <code>'n'</code> - null |
||||
|
* <code>'-', '0'..'9'</code> - number |
||||
|
|
||||
|
Token is an object of `jsmntok_t` type: |
||||
|
|
||||
|
typedef struct { |
||||
|
jsmntype_t type; // Token type |
||||
|
int start; // Token start position |
||||
|
int end; // Token end position |
||||
|
int size; // Number of child (nested) tokens |
||||
|
} jsmntok_t; |
||||
|
|
||||
|
**Note:** string tokens point to the first character after |
||||
|
the opening quote and the previous symbol before final quote. This was made |
||||
|
to simplify string extraction from JSON data. |
||||
|
|
||||
|
All job is done by `jsmn_parser` object. You can initialize a new parser using: |
||||
|
|
||||
|
jsmn_parser parser; |
||||
|
jsmntok_t tokens[10]; |
||||
|
|
||||
|
jsmn_init(&parser); |
||||
|
|
||||
|
// js - pointer to JSON string |
||||
|
// tokens - an array of tokens available |
||||
|
// 10 - number of tokens available |
||||
|
jsmn_parse(&parser, js, strlen(js), tokens, 10); |
||||
|
|
||||
|
This will create a parser, and then it tries to parse up to 10 JSON tokens from |
||||
|
the `js` string. |
||||
|
|
||||
|
A non-negative return value of `jsmn_parse` is the number of tokens actually |
||||
|
used by the parser. |
||||
|
Passing NULL instead of the tokens array would not store parsing results, but |
||||
|
instead the function will return the value of tokens needed to parse the given |
||||
|
string. This can be useful if you don't know yet how many tokens to allocate. |
||||
|
|
||||
|
If something goes wrong, you will get an error. Error will be one of these: |
||||
|
|
||||
|
* `JSMN_ERROR_INVAL` - bad token, JSON string is corrupted |
||||
|
* `JSMN_ERROR_NOMEM` - not enough tokens, JSON string is too large |
||||
|
* `JSMN_ERROR_PART` - JSON string is too short, expecting more JSON data |
||||
|
|
||||
|
If you get `JSMN_ERROR_NOMEM`, you can re-allocate more tokens and call |
||||
|
`jsmn_parse` once more. If you read json data from the stream, you can |
||||
|
periodically call `jsmn_parse` and check if return value is `JSMN_ERROR_PART`. |
||||
|
You will get this error until you reach the end of JSON data. |
||||
|
|
||||
|
Other info |
||||
|
---------- |
||||
|
|
||||
|
This software is distributed under [MIT license](http://www.opensource.org/licenses/mit-license.php), |
||||
|
so feel free to integrate it in your commercial products. |
||||
|
|
||||
|
[1]: http://www.json.org/ |
||||
|
[2]: http://zserge.com/jsmn.html |
@ -0,0 +1,126 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
#include <string.h> |
||||
|
#include <errno.h> |
||||
|
#include "../jsmn.h" |
||||
|
|
||||
|
/* Function realloc_it() is a wrapper function for standard realloc()
|
||||
|
* with one difference - it frees old memory pointer in case of realloc |
||||
|
* failure. Thus, DO NOT use old data pointer in anyway after call to |
||||
|
* realloc_it(). If your code has some kind of fallback algorithm if |
||||
|
* memory can't be re-allocated - use standard realloc() instead. |
||||
|
*/ |
||||
|
static inline void *realloc_it(void *ptrmem, size_t size) { |
||||
|
void *p = realloc(ptrmem, size); |
||||
|
if (!p) { |
||||
|
free (ptrmem); |
||||
|
fprintf(stderr, "realloc(): errno=%d\n", errno); |
||||
|
} |
||||
|
return p; |
||||
|
} |
||||
|
|
||||
|
/*
|
||||
|
* An example of reading JSON from stdin and printing its content to stdout. |
||||
|
* The output looks like YAML, but I'm not sure if it's really compatible. |
||||
|
*/ |
||||
|
|
||||
|
static int dump(const char *js, jsmntok_t *t, size_t count, int indent) { |
||||
|
int i, j, k; |
||||
|
if (count == 0) { |
||||
|
return 0; |
||||
|
} |
||||
|
if (t->type == JSMN_PRIMITIVE) { |
||||
|
printf("%.*s", t->end - t->start, js+t->start); |
||||
|
return 1; |
||||
|
} else if (t->type == JSMN_STRING) { |
||||
|
printf("'%.*s'", t->end - t->start, js+t->start); |
||||
|
return 1; |
||||
|
} else if (t->type == JSMN_OBJECT) { |
||||
|
printf("\n"); |
||||
|
j = 0; |
||||
|
for (i = 0; i < t->size; i++) { |
||||
|
for (k = 0; k < indent; k++) printf(" "); |
||||
|
j += dump(js, t+1+j, count-j, indent+1); |
||||
|
printf(": "); |
||||
|
j += dump(js, t+1+j, count-j, indent+1); |
||||
|
printf("\n"); |
||||
|
} |
||||
|
return j+1; |
||||
|
} else if (t->type == JSMN_ARRAY) { |
||||
|
j = 0; |
||||
|
printf("\n"); |
||||
|
for (i = 0; i < t->size; i++) { |
||||
|
for (k = 0; k < indent-1; k++) printf(" "); |
||||
|
printf(" - "); |
||||
|
j += dump(js, t+1+j, count-j, indent+1); |
||||
|
printf("\n"); |
||||
|
} |
||||
|
return j+1; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int main() { |
||||
|
int r; |
||||
|
int eof_expected = 0; |
||||
|
char *js = NULL; |
||||
|
size_t jslen = 0; |
||||
|
char buf[BUFSIZ]; |
||||
|
|
||||
|
jsmn_parser p; |
||||
|
jsmntok_t *tok; |
||||
|
size_t tokcount = 2; |
||||
|
|
||||
|
/* Prepare parser */ |
||||
|
jsmn_init(&p); |
||||
|
|
||||
|
/* Allocate some tokens as a start */ |
||||
|
tok = malloc(sizeof(*tok) * tokcount); |
||||
|
if (tok == NULL) { |
||||
|
fprintf(stderr, "malloc(): errno=%d\n", errno); |
||||
|
return 3; |
||||
|
} |
||||
|
|
||||
|
for (;;) { |
||||
|
/* Read another chunk */ |
||||
|
r = fread(buf, 1, sizeof(buf), stdin); |
||||
|
if (r < 0) { |
||||
|
fprintf(stderr, "fread(): %d, errno=%d\n", r, errno); |
||||
|
return 1; |
||||
|
} |
||||
|
if (r == 0) { |
||||
|
if (eof_expected != 0) { |
||||
|
return 0; |
||||
|
} else { |
||||
|
fprintf(stderr, "fread(): unexpected EOF\n"); |
||||
|
return 2; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
js = realloc_it(js, jslen + r + 1); |
||||
|
if (js == NULL) { |
||||
|
return 3; |
||||
|
} |
||||
|
strncpy(js + jslen, buf, r); |
||||
|
jslen = jslen + r; |
||||
|
|
||||
|
again: |
||||
|
r = jsmn_parse(&p, js, jslen, tok, tokcount); |
||||
|
if (r < 0) { |
||||
|
if (r == JSMN_ERROR_NOMEM) { |
||||
|
tokcount = tokcount * 2; |
||||
|
tok = realloc_it(tok, sizeof(*tok) * tokcount); |
||||
|
if (tok == NULL) { |
||||
|
return 3; |
||||
|
} |
||||
|
goto again; |
||||
|
} |
||||
|
} else { |
||||
|
dump(js, tok, p.toknext, 0); |
||||
|
eof_expected = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return EXIT_SUCCESS; |
||||
|
} |
@ -0,0 +1,76 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include "../jsmn.h" |
||||
|
|
||||
|
/*
|
||||
|
* A small example of jsmn parsing when JSON structure is known and number of |
||||
|
* tokens is predictable. |
||||
|
*/ |
||||
|
|
||||
|
static const char *JSON_STRING = |
||||
|
"{\"user\": \"johndoe\", \"admin\": false, \"uid\": 1000,\n " |
||||
|
"\"groups\": [\"users\", \"wheel\", \"audio\", \"video\"]}"; |
||||
|
|
||||
|
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) { |
||||
|
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && |
||||
|
strncmp(json + tok->start, s, tok->end - tok->start) == 0) { |
||||
|
return 0; |
||||
|
} |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
int main() { |
||||
|
int i; |
||||
|
int r; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t t[128]; /* We expect no more than 128 tokens */ |
||||
|
|
||||
|
jsmn_init(&p); |
||||
|
r = jsmn_parse(&p, JSON_STRING, strlen(JSON_STRING), t, sizeof(t)/sizeof(t[0])); |
||||
|
if (r < 0) { |
||||
|
printf("Failed to parse JSON: %d\n", r); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/* Assume the top-level element is an object */ |
||||
|
if (r < 1 || t[0].type != JSMN_OBJECT) { |
||||
|
printf("Object expected\n"); |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/* Loop over all keys of the root object */ |
||||
|
for (i = 1; i < r; i++) { |
||||
|
if (jsoneq(JSON_STRING, &t[i], "user") == 0) { |
||||
|
/* We may use strndup() to fetch string value */ |
||||
|
printf("- User: %.*s\n", t[i+1].end-t[i+1].start, |
||||
|
JSON_STRING + t[i+1].start); |
||||
|
i++; |
||||
|
} else if (jsoneq(JSON_STRING, &t[i], "admin") == 0) { |
||||
|
/* We may additionally check if the value is either "true" or "false" */ |
||||
|
printf("- Admin: %.*s\n", t[i+1].end-t[i+1].start, |
||||
|
JSON_STRING + t[i+1].start); |
||||
|
i++; |
||||
|
} else if (jsoneq(JSON_STRING, &t[i], "uid") == 0) { |
||||
|
/* We may want to do strtol() here to get numeric value */ |
||||
|
printf("- UID: %.*s\n", t[i+1].end-t[i+1].start, |
||||
|
JSON_STRING + t[i+1].start); |
||||
|
i++; |
||||
|
} else if (jsoneq(JSON_STRING, &t[i], "groups") == 0) { |
||||
|
int j; |
||||
|
printf("- Groups:\n"); |
||||
|
if (t[i+1].type != JSMN_ARRAY) { |
||||
|
continue; /* We expect groups to be an array of strings */ |
||||
|
} |
||||
|
for (j = 0; j < t[i+1].size; j++) { |
||||
|
jsmntok_t *g = &t[i+j+2]; |
||||
|
printf(" * %.*s\n", g->end - g->start, JSON_STRING + g->start); |
||||
|
} |
||||
|
i += t[i+1].size + 1; |
||||
|
} else { |
||||
|
printf("Unexpected key: %.*s\n", t[i].end-t[i].start, |
||||
|
JSON_STRING + t[i].start); |
||||
|
} |
||||
|
} |
||||
|
return EXIT_SUCCESS; |
||||
|
} |
@ -0,0 +1,314 @@ |
|||||
|
#include "jsmn.h" |
||||
|
|
||||
|
/**
|
||||
|
* Allocates a fresh unused token from the token pool. |
||||
|
*/ |
||||
|
static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, |
||||
|
jsmntok_t *tokens, size_t num_tokens) { |
||||
|
jsmntok_t *tok; |
||||
|
if (parser->toknext >= num_tokens) { |
||||
|
return NULL; |
||||
|
} |
||||
|
tok = &tokens[parser->toknext++]; |
||||
|
tok->start = tok->end = -1; |
||||
|
tok->size = 0; |
||||
|
#ifdef JSMN_PARENT_LINKS |
||||
|
tok->parent = -1; |
||||
|
#endif |
||||
|
return tok; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Fills token type and boundaries. |
||||
|
*/ |
||||
|
static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, |
||||
|
int start, int end) { |
||||
|
token->type = type; |
||||
|
token->start = start; |
||||
|
token->end = end; |
||||
|
token->size = 0; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Fills next available token with JSON primitive. |
||||
|
*/ |
||||
|
static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, |
||||
|
size_t len, jsmntok_t *tokens, size_t num_tokens) { |
||||
|
jsmntok_t *token; |
||||
|
int start; |
||||
|
|
||||
|
start = parser->pos; |
||||
|
|
||||
|
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { |
||||
|
switch (js[parser->pos]) { |
||||
|
#ifndef JSMN_STRICT |
||||
|
/* In strict mode primitive must be followed by "," or "}" or "]" */ |
||||
|
case ':': |
||||
|
#endif |
||||
|
case '\t' : case '\r' : case '\n' : case ' ' : |
||||
|
case ',' : case ']' : case '}' : |
||||
|
goto found; |
||||
|
} |
||||
|
if (js[parser->pos] < 32 || js[parser->pos] >= 127) { |
||||
|
parser->pos = start; |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
} |
||||
|
#ifdef JSMN_STRICT |
||||
|
/* In strict mode primitive must be followed by a comma/object/array */ |
||||
|
parser->pos = start; |
||||
|
return JSMN_ERROR_PART; |
||||
|
#endif |
||||
|
|
||||
|
found: |
||||
|
if (tokens == NULL) { |
||||
|
parser->pos--; |
||||
|
return 0; |
||||
|
} |
||||
|
token = jsmn_alloc_token(parser, tokens, num_tokens); |
||||
|
if (token == NULL) { |
||||
|
parser->pos = start; |
||||
|
return JSMN_ERROR_NOMEM; |
||||
|
} |
||||
|
jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); |
||||
|
#ifdef JSMN_PARENT_LINKS |
||||
|
token->parent = parser->toksuper; |
||||
|
#endif |
||||
|
parser->pos--; |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Fills next token with JSON string. |
||||
|
*/ |
||||
|
static int jsmn_parse_string(jsmn_parser *parser, const char *js, |
||||
|
size_t len, jsmntok_t *tokens, size_t num_tokens) { |
||||
|
jsmntok_t *token; |
||||
|
|
||||
|
int start = parser->pos; |
||||
|
|
||||
|
parser->pos++; |
||||
|
|
||||
|
/* Skip starting quote */ |
||||
|
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { |
||||
|
char c = js[parser->pos]; |
||||
|
|
||||
|
/* Quote: end of string */ |
||||
|
if (c == '\"') { |
||||
|
if (tokens == NULL) { |
||||
|
return 0; |
||||
|
} |
||||
|
token = jsmn_alloc_token(parser, tokens, num_tokens); |
||||
|
if (token == NULL) { |
||||
|
parser->pos = start; |
||||
|
return JSMN_ERROR_NOMEM; |
||||
|
} |
||||
|
jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); |
||||
|
#ifdef JSMN_PARENT_LINKS |
||||
|
token->parent = parser->toksuper; |
||||
|
#endif |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* Backslash: Quoted symbol expected */ |
||||
|
if (c == '\\' && parser->pos + 1 < len) { |
||||
|
int i; |
||||
|
parser->pos++; |
||||
|
switch (js[parser->pos]) { |
||||
|
/* Allowed escaped symbols */ |
||||
|
case '\"': case '/' : case '\\' : case 'b' : |
||||
|
case 'f' : case 'r' : case 'n' : case 't' : |
||||
|
break; |
||||
|
/* Allows escaped symbol \uXXXX */ |
||||
|
case 'u': |
||||
|
parser->pos++; |
||||
|
for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { |
||||
|
/* If it isn't a hex character we have an error */ |
||||
|
if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ |
||||
|
(js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ |
||||
|
(js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ |
||||
|
parser->pos = start; |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
parser->pos++; |
||||
|
} |
||||
|
parser->pos--; |
||||
|
break; |
||||
|
/* Unexpected symbol */ |
||||
|
default: |
||||
|
parser->pos = start; |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
parser->pos = start; |
||||
|
return JSMN_ERROR_PART; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Parse JSON string and fill tokens. |
||||
|
*/ |
||||
|
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, |
||||
|
jsmntok_t *tokens, unsigned int num_tokens) { |
||||
|
int r; |
||||
|
int i; |
||||
|
jsmntok_t *token; |
||||
|
int count = parser->toknext; |
||||
|
|
||||
|
for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { |
||||
|
char c; |
||||
|
jsmntype_t type; |
||||
|
|
||||
|
c = js[parser->pos]; |
||||
|
switch (c) { |
||||
|
case '{': case '[': |
||||
|
count++; |
||||
|
if (tokens == NULL) { |
||||
|
break; |
||||
|
} |
||||
|
token = jsmn_alloc_token(parser, tokens, num_tokens); |
||||
|
if (token == NULL) |
||||
|
return JSMN_ERROR_NOMEM; |
||||
|
if (parser->toksuper != -1) { |
||||
|
tokens[parser->toksuper].size++; |
||||
|
#ifdef JSMN_PARENT_LINKS |
||||
|
token->parent = parser->toksuper; |
||||
|
#endif |
||||
|
} |
||||
|
token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); |
||||
|
token->start = parser->pos; |
||||
|
parser->toksuper = parser->toknext - 1; |
||||
|
break; |
||||
|
case '}': case ']': |
||||
|
if (tokens == NULL) |
||||
|
break; |
||||
|
type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); |
||||
|
#ifdef JSMN_PARENT_LINKS |
||||
|
if (parser->toknext < 1) { |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
token = &tokens[parser->toknext - 1]; |
||||
|
for (;;) { |
||||
|
if (token->start != -1 && token->end == -1) { |
||||
|
if (token->type != type) { |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
token->end = parser->pos + 1; |
||||
|
parser->toksuper = token->parent; |
||||
|
break; |
||||
|
} |
||||
|
if (token->parent == -1) { |
||||
|
if(token->type != type || parser->toksuper == -1) { |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
token = &tokens[token->parent]; |
||||
|
} |
||||
|
#else |
||||
|
for (i = parser->toknext - 1; i >= 0; i--) { |
||||
|
token = &tokens[i]; |
||||
|
if (token->start != -1 && token->end == -1) { |
||||
|
if (token->type != type) { |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
parser->toksuper = -1; |
||||
|
token->end = parser->pos + 1; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
/* Error if unmatched closing bracket */ |
||||
|
if (i == -1) return JSMN_ERROR_INVAL; |
||||
|
for (; i >= 0; i--) { |
||||
|
token = &tokens[i]; |
||||
|
if (token->start != -1 && token->end == -1) { |
||||
|
parser->toksuper = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
break; |
||||
|
case '\"': |
||||
|
r = jsmn_parse_string(parser, js, len, tokens, num_tokens); |
||||
|
if (r < 0) return r; |
||||
|
count++; |
||||
|
if (parser->toksuper != -1 && tokens != NULL) |
||||
|
tokens[parser->toksuper].size++; |
||||
|
break; |
||||
|
case '\t' : case '\r' : case '\n' : case ' ': |
||||
|
break; |
||||
|
case ':': |
||||
|
parser->toksuper = parser->toknext - 1; |
||||
|
break; |
||||
|
case ',': |
||||
|
if (tokens != NULL && parser->toksuper != -1 && |
||||
|
tokens[parser->toksuper].type != JSMN_ARRAY && |
||||
|
tokens[parser->toksuper].type != JSMN_OBJECT) { |
||||
|
#ifdef JSMN_PARENT_LINKS |
||||
|
parser->toksuper = tokens[parser->toksuper].parent; |
||||
|
#else |
||||
|
for (i = parser->toknext - 1; i >= 0; i--) { |
||||
|
if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { |
||||
|
if (tokens[i].start != -1 && tokens[i].end == -1) { |
||||
|
parser->toksuper = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
} |
||||
|
break; |
||||
|
#ifdef JSMN_STRICT |
||||
|
/* In strict mode primitives are: numbers and booleans */ |
||||
|
case '-': case '0': case '1' : case '2': case '3' : case '4': |
||||
|
case '5': case '6': case '7' : case '8': case '9': |
||||
|
case 't': case 'f': case 'n' : |
||||
|
/* And they must not be keys of the object */ |
||||
|
if (tokens != NULL && parser->toksuper != -1) { |
||||
|
jsmntok_t *t = &tokens[parser->toksuper]; |
||||
|
if (t->type == JSMN_OBJECT || |
||||
|
(t->type == JSMN_STRING && t->size != 0)) { |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
} |
||||
|
} |
||||
|
#else |
||||
|
/* In non-strict mode every unquoted value is a primitive */ |
||||
|
default: |
||||
|
#endif |
||||
|
r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); |
||||
|
if (r < 0) return r; |
||||
|
count++; |
||||
|
if (parser->toksuper != -1 && tokens != NULL) |
||||
|
tokens[parser->toksuper].size++; |
||||
|
break; |
||||
|
|
||||
|
#ifdef JSMN_STRICT |
||||
|
/* Unexpected char in strict mode */ |
||||
|
default: |
||||
|
return JSMN_ERROR_INVAL; |
||||
|
#endif |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (tokens != NULL) { |
||||
|
for (i = parser->toknext - 1; i >= 0; i--) { |
||||
|
/* Unmatched opened object or array */ |
||||
|
if (tokens[i].start != -1 && tokens[i].end == -1) { |
||||
|
return JSMN_ERROR_PART; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return count; |
||||
|
} |
||||
|
|
||||
|
/**
|
||||
|
* Creates a new parser based over a given buffer with an array of tokens |
||||
|
* available. |
||||
|
*/ |
||||
|
void jsmn_init(jsmn_parser *parser) { |
||||
|
parser->pos = 0; |
||||
|
parser->toknext = 0; |
||||
|
parser->toksuper = -1; |
||||
|
} |
||||
|
|
@ -0,0 +1,76 @@ |
|||||
|
#ifndef __JSMN_H_ |
||||
|
#define __JSMN_H_ |
||||
|
|
||||
|
#include <stddef.h> |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
/**
|
||||
|
* JSON type identifier. Basic types are: |
||||
|
* o Object |
||||
|
* o Array |
||||
|
* o String |
||||
|
* o Other primitive: number, boolean (true/false) or null |
||||
|
*/ |
||||
|
typedef enum { |
||||
|
JSMN_UNDEFINED = 0, |
||||
|
JSMN_OBJECT = 1, |
||||
|
JSMN_ARRAY = 2, |
||||
|
JSMN_STRING = 3, |
||||
|
JSMN_PRIMITIVE = 4 |
||||
|
} jsmntype_t; |
||||
|
|
||||
|
enum jsmnerr { |
||||
|
/* Not enough tokens were provided */ |
||||
|
JSMN_ERROR_NOMEM = -1, |
||||
|
/* Invalid character inside JSON string */ |
||||
|
JSMN_ERROR_INVAL = -2, |
||||
|
/* The string is not a full JSON packet, more bytes expected */ |
||||
|
JSMN_ERROR_PART = -3 |
||||
|
}; |
||||
|
|
||||
|
/**
|
||||
|
* JSON token description. |
||||
|
* type type (object, array, string etc.) |
||||
|
* start start position in JSON data string |
||||
|
* end end position in JSON data string |
||||
|
*/ |
||||
|
typedef struct { |
||||
|
jsmntype_t type; |
||||
|
int start; |
||||
|
int end; |
||||
|
int size; |
||||
|
#ifdef JSMN_PARENT_LINKS |
||||
|
int parent; |
||||
|
#endif |
||||
|
} jsmntok_t; |
||||
|
|
||||
|
/**
|
||||
|
* JSON parser. Contains an array of token blocks available. Also stores |
||||
|
* the string being parsed now and current position in that string |
||||
|
*/ |
||||
|
typedef struct { |
||||
|
unsigned int pos; /* offset in the JSON string */ |
||||
|
unsigned int toknext; /* next token to allocate */ |
||||
|
int toksuper; /* superior token node, e.g parent object or array */ |
||||
|
} jsmn_parser; |
||||
|
|
||||
|
/**
|
||||
|
* Create JSON parser over an array of tokens |
||||
|
*/ |
||||
|
void jsmn_init(jsmn_parser *parser); |
||||
|
|
||||
|
/**
|
||||
|
* Run JSON parser. It parses a JSON data string into and array of tokens, each describing |
||||
|
* a single JSON object. |
||||
|
*/ |
||||
|
int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, |
||||
|
jsmntok_t *tokens, unsigned int num_tokens); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
#endif /* __JSMN_H_ */ |
@ -0,0 +1,16 @@ |
|||||
|
{ |
||||
|
"name": "jsmn", |
||||
|
"keywords": "json", |
||||
|
"description": "Minimalistic JSON parser/tokenizer in C. It can be easily integrated into resource-limited or embedded projects", |
||||
|
"repository": |
||||
|
{ |
||||
|
"type": "git", |
||||
|
"url": "https://github.com/zserge/jsmn.git" |
||||
|
}, |
||||
|
"frameworks": "*", |
||||
|
"platforms": "*", |
||||
|
"examples": [ |
||||
|
"example/*.c" |
||||
|
], |
||||
|
"exclude": "test" |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
#ifndef __TEST_H__ |
||||
|
#define __TEST_H__ |
||||
|
|
||||
|
static int test_passed = 0; |
||||
|
static int test_failed = 0; |
||||
|
|
||||
|
/* Terminate current test with error */ |
||||
|
#define fail() return __LINE__ |
||||
|
|
||||
|
/* Successful end of the test case */ |
||||
|
#define done() return 0 |
||||
|
|
||||
|
/* Check single condition */ |
||||
|
#define check(cond) do { if (!(cond)) fail(); } while (0) |
||||
|
|
||||
|
/* Test runner */ |
||||
|
static void test(int (*func)(void), const char *name) { |
||||
|
int r = func(); |
||||
|
if (r == 0) { |
||||
|
test_passed++; |
||||
|
} else { |
||||
|
test_failed++; |
||||
|
printf("FAILED: %s (at line %d)\n", name, r); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#endif /* __TEST_H__ */ |
@ -0,0 +1,407 @@ |
|||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <stdarg.h> |
||||
|
|
||||
|
#include "test.h" |
||||
|
#include "testutil.h" |
||||
|
|
||||
|
int test_empty(void) { |
||||
|
check(parse("{}", 1, 1, |
||||
|
JSMN_OBJECT, 0, 2, 0)); |
||||
|
check(parse("[]", 1, 1, |
||||
|
JSMN_ARRAY, 0, 2, 0)); |
||||
|
check(parse("[{},{}]", 3, 3, |
||||
|
JSMN_ARRAY, 0, 7, 2, |
||||
|
JSMN_OBJECT, 1, 3, 0, |
||||
|
JSMN_OBJECT, 4, 6, 0)); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_object(void) { |
||||
|
check(parse("{\"a\":0}", 3, 3, |
||||
|
JSMN_OBJECT, 0, 7, 1, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_PRIMITIVE, "0")); |
||||
|
check(parse("{\"a\":[]}", 3, 3, |
||||
|
JSMN_OBJECT, 0, 8, 1, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_ARRAY, 5, 7, 0)); |
||||
|
check(parse("{\"a\":{},\"b\":{}}", 5, 5, |
||||
|
JSMN_OBJECT, -1, -1, 2, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_OBJECT, -1, -1, 0, |
||||
|
JSMN_STRING, "b", 1, |
||||
|
JSMN_OBJECT, -1, -1, 0)); |
||||
|
check(parse("{\n \"Day\": 26,\n \"Month\": 9,\n \"Year\": 12\n }", 7, 7, |
||||
|
JSMN_OBJECT, -1, -1, 3, |
||||
|
JSMN_STRING, "Day", 1, |
||||
|
JSMN_PRIMITIVE, "26", |
||||
|
JSMN_STRING, "Month", 1, |
||||
|
JSMN_PRIMITIVE, "9", |
||||
|
JSMN_STRING, "Year", 1, |
||||
|
JSMN_PRIMITIVE, "12")); |
||||
|
check(parse("{\"a\": 0, \"b\": \"c\"}", 5, 5, |
||||
|
JSMN_OBJECT, -1, -1, 2, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_PRIMITIVE, "0", |
||||
|
JSMN_STRING, "b", 1, |
||||
|
JSMN_STRING, "c", 0)); |
||||
|
|
||||
|
#ifdef JSMN_STRICT |
||||
|
check(parse("{\"a\"\n0}", JSMN_ERROR_INVAL, 3)); |
||||
|
check(parse("{\"a\", 0}", JSMN_ERROR_INVAL, 3)); |
||||
|
check(parse("{\"a\": {2}}", JSMN_ERROR_INVAL, 3)); |
||||
|
check(parse("{\"a\": {2: 3}}", JSMN_ERROR_INVAL, 3)); |
||||
|
check(parse("{\"a\": {\"a\": 2 3}}", JSMN_ERROR_INVAL, 5)); |
||||
|
/* FIXME */ |
||||
|
/*check(parse("{\"a\"}", JSMN_ERROR_INVAL, 2));*/ |
||||
|
/*check(parse("{\"a\": 1, \"b\"}", JSMN_ERROR_INVAL, 4));*/ |
||||
|
/*check(parse("{\"a\",\"b\":1}", JSMN_ERROR_INVAL, 4));*/ |
||||
|
/*check(parse("{\"a\":1,}", JSMN_ERROR_INVAL, 4));*/ |
||||
|
/*check(parse("{\"a\":\"b\":\"c\"}", JSMN_ERROR_INVAL, 4));*/ |
||||
|
/*check(parse("{,}", JSMN_ERROR_INVAL, 4));*/ |
||||
|
#endif |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_array(void) { |
||||
|
/* FIXME */ |
||||
|
/*check(parse("[10}", JSMN_ERROR_INVAL, 3));*/ |
||||
|
/*check(parse("[1,,3]", JSMN_ERROR_INVAL, 3)*/ |
||||
|
check(parse("[10]", 2, 2, |
||||
|
JSMN_ARRAY, -1, -1, 1, |
||||
|
JSMN_PRIMITIVE, "10")); |
||||
|
check(parse("{\"a\": 1]", JSMN_ERROR_INVAL, 3)); |
||||
|
/* FIXME */ |
||||
|
/*check(parse("[\"a\": 1]", JSMN_ERROR_INVAL, 3));*/ |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_primitive(void) { |
||||
|
check(parse("{\"boolVar\" : true }", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "boolVar", 1, |
||||
|
JSMN_PRIMITIVE, "true")); |
||||
|
check(parse("{\"boolVar\" : false }", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "boolVar", 1, |
||||
|
JSMN_PRIMITIVE, "false")); |
||||
|
check(parse("{\"nullVar\" : null }", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "nullVar", 1, |
||||
|
JSMN_PRIMITIVE, "null")); |
||||
|
check(parse("{\"intVar\" : 12}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "intVar", 1, |
||||
|
JSMN_PRIMITIVE, "12")); |
||||
|
check(parse("{\"floatVar\" : 12.345}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "floatVar", 1, |
||||
|
JSMN_PRIMITIVE, "12.345")); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_string(void) { |
||||
|
check(parse("{\"strVar\" : \"hello world\"}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "strVar", 1, |
||||
|
JSMN_STRING, "hello world", 0)); |
||||
|
check(parse("{\"strVar\" : \"escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\\"}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "strVar", 1, |
||||
|
JSMN_STRING, "escapes: \\/\\r\\n\\t\\b\\f\\\"\\\\", 0)); |
||||
|
check(parse("{\"strVar\": \"\"}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "strVar", 1, |
||||
|
JSMN_STRING, "", 0)); |
||||
|
check(parse("{\"a\":\"\\uAbcD\"}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_STRING, "\\uAbcD", 0)); |
||||
|
check(parse("{\"a\":\"str\\u0000\"}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_STRING, "str\\u0000", 0)); |
||||
|
check(parse("{\"a\":\"\\uFFFFstr\"}", 3, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_STRING, "\\uFFFFstr", 0)); |
||||
|
check(parse("{\"a\":[\"\\u0280\"]}", 4, 4, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_ARRAY, -1, -1, 1, |
||||
|
JSMN_STRING, "\\u0280", 0)); |
||||
|
|
||||
|
check(parse("{\"a\":\"str\\uFFGFstr\"}", JSMN_ERROR_INVAL, 3)); |
||||
|
check(parse("{\"a\":\"str\\u@FfF\"}", JSMN_ERROR_INVAL, 3)); |
||||
|
check(parse("{{\"a\":[\"\\u028\"]}", JSMN_ERROR_INVAL, 4)); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_partial_string(void) { |
||||
|
int i; |
||||
|
int r; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t tok[5]; |
||||
|
const char *js = "{\"x\": \"va\\\\ue\", \"y\": \"value y\"}"; |
||||
|
|
||||
|
jsmn_init(&p); |
||||
|
for (i = 1; i <= strlen(js); i++) { |
||||
|
r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0])); |
||||
|
if (i == strlen(js)) { |
||||
|
check(r == 5); |
||||
|
check(tokeq(js, tok, 5, |
||||
|
JSMN_OBJECT, -1, -1, 2, |
||||
|
JSMN_STRING, "x", 1, |
||||
|
JSMN_STRING, "va\\\\ue", 0, |
||||
|
JSMN_STRING, "y", 1, |
||||
|
JSMN_STRING, "value y", 0)); |
||||
|
} else { |
||||
|
check(r == JSMN_ERROR_PART); |
||||
|
} |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_partial_array(void) { |
||||
|
#ifdef JSMN_STRICT |
||||
|
int r; |
||||
|
int i; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t tok[10]; |
||||
|
const char *js = "[ 1, true, [123, \"hello\"]]"; |
||||
|
|
||||
|
jsmn_init(&p); |
||||
|
for (i = 1; i <= strlen(js); i++) { |
||||
|
r = jsmn_parse(&p, js, i, tok, sizeof(tok)/sizeof(tok[0])); |
||||
|
if (i == strlen(js)) { |
||||
|
check(r == 6); |
||||
|
check(tokeq(js, tok, 6, |
||||
|
JSMN_ARRAY, -1, -1, 3, |
||||
|
JSMN_PRIMITIVE, "1", |
||||
|
JSMN_PRIMITIVE, "true", |
||||
|
JSMN_ARRAY, -1, -1, 2, |
||||
|
JSMN_PRIMITIVE, "123", |
||||
|
JSMN_STRING, "hello", 0)); |
||||
|
} else { |
||||
|
check(r == JSMN_ERROR_PART); |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_array_nomem(void) { |
||||
|
int i; |
||||
|
int r; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t toksmall[10], toklarge[10]; |
||||
|
const char *js; |
||||
|
|
||||
|
js = " [ 1, true, [123, \"hello\"]]"; |
||||
|
|
||||
|
for (i = 0; i < 6; i++) { |
||||
|
jsmn_init(&p); |
||||
|
memset(toksmall, 0, sizeof(toksmall)); |
||||
|
memset(toklarge, 0, sizeof(toklarge)); |
||||
|
r = jsmn_parse(&p, js, strlen(js), toksmall, i); |
||||
|
check(r == JSMN_ERROR_NOMEM); |
||||
|
|
||||
|
memcpy(toklarge, toksmall, sizeof(toksmall)); |
||||
|
|
||||
|
r = jsmn_parse(&p, js, strlen(js), toklarge, 10); |
||||
|
check(r >= 0); |
||||
|
check(tokeq(js, toklarge, 4, |
||||
|
JSMN_ARRAY, -1, -1, 3, |
||||
|
JSMN_PRIMITIVE, "1", |
||||
|
JSMN_PRIMITIVE, "true", |
||||
|
JSMN_ARRAY, -1, -1, 2, |
||||
|
JSMN_PRIMITIVE, "123", |
||||
|
JSMN_STRING, "hello", 0)); |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_unquoted_keys(void) { |
||||
|
#ifndef JSMN_STRICT |
||||
|
int r; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t tok[10]; |
||||
|
const char *js; |
||||
|
|
||||
|
jsmn_init(&p); |
||||
|
js = "key1: \"value\"\nkey2 : 123"; |
||||
|
|
||||
|
r = jsmn_parse(&p, js, strlen(js), tok, 10); |
||||
|
check(r >= 0); |
||||
|
check(tokeq(js, tok, 4, |
||||
|
JSMN_PRIMITIVE, "key1", |
||||
|
JSMN_STRING, "value", 0, |
||||
|
JSMN_PRIMITIVE, "key2", |
||||
|
JSMN_PRIMITIVE, "123")); |
||||
|
#endif |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_issue_22(void) { |
||||
|
int r; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t tokens[128]; |
||||
|
const char *js; |
||||
|
|
||||
|
js = "{ \"height\":10, \"layers\":[ { \"data\":[6,6], \"height\":10, " |
||||
|
"\"name\":\"Calque de Tile 1\", \"opacity\":1, \"type\":\"tilelayer\", " |
||||
|
"\"visible\":true, \"width\":10, \"x\":0, \"y\":0 }], " |
||||
|
"\"orientation\":\"orthogonal\", \"properties\": { }, \"tileheight\":32, " |
||||
|
"\"tilesets\":[ { \"firstgid\":1, \"image\":\"..\\/images\\/tiles.png\", " |
||||
|
"\"imageheight\":64, \"imagewidth\":160, \"margin\":0, \"name\":\"Tiles\", " |
||||
|
"\"properties\":{}, \"spacing\":0, \"tileheight\":32, \"tilewidth\":32 }], " |
||||
|
"\"tilewidth\":32, \"version\":1, \"width\":10 }"; |
||||
|
jsmn_init(&p); |
||||
|
r = jsmn_parse(&p, js, strlen(js), tokens, 128); |
||||
|
check(r >= 0); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_issue_27(void) { |
||||
|
const char *js = |
||||
|
"{ \"name\" : \"Jack\", \"age\" : 27 } { \"name\" : \"Anna\", "; |
||||
|
check(parse(js, JSMN_ERROR_PART, 8)); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_input_length(void) { |
||||
|
const char *js; |
||||
|
int r; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t tokens[10]; |
||||
|
|
||||
|
js = "{\"a\": 0}garbage"; |
||||
|
|
||||
|
jsmn_init(&p); |
||||
|
r = jsmn_parse(&p, js, 8, tokens, 10); |
||||
|
check(r == 3); |
||||
|
check(tokeq(js, tokens, 3, |
||||
|
JSMN_OBJECT, -1, -1, 1, |
||||
|
JSMN_STRING, "a", 1, |
||||
|
JSMN_PRIMITIVE, "0")); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_count(void) { |
||||
|
jsmn_parser p; |
||||
|
const char *js; |
||||
|
|
||||
|
js = "{}"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); |
||||
|
|
||||
|
js = "[]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 1); |
||||
|
|
||||
|
js = "[[]]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 2); |
||||
|
|
||||
|
js = "[[], []]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); |
||||
|
|
||||
|
js = "[[], []]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 3); |
||||
|
|
||||
|
js = "[[], [[]], [[], []]]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); |
||||
|
|
||||
|
js = "[\"a\", [[], []]]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); |
||||
|
|
||||
|
js = "[[], \"[], [[]]\", [[]]]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 5); |
||||
|
|
||||
|
js = "[1, 2, 3]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 4); |
||||
|
|
||||
|
js = "[1, 2, [3, \"a\"], null]"; |
||||
|
jsmn_init(&p); |
||||
|
check(jsmn_parse(&p, js, strlen(js), NULL, 0) == 7); |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
int test_nonstrict(void) { |
||||
|
#ifndef JSMN_STRICT |
||||
|
const char *js; |
||||
|
js = "a: 0garbage"; |
||||
|
check(parse(js, 2, 2, |
||||
|
JSMN_PRIMITIVE, "a", |
||||
|
JSMN_PRIMITIVE, "0garbage")); |
||||
|
|
||||
|
js = "Day : 26\nMonth : Sep\n\nYear: 12"; |
||||
|
check(parse(js, 6, 6, |
||||
|
JSMN_PRIMITIVE, "Day", |
||||
|
JSMN_PRIMITIVE, "26", |
||||
|
JSMN_PRIMITIVE, "Month", |
||||
|
JSMN_PRIMITIVE, "Sep", |
||||
|
JSMN_PRIMITIVE, "Year", |
||||
|
JSMN_PRIMITIVE, "12")); |
||||
|
|
||||
|
//nested {s don't cause a parse error.
|
||||
|
js = "\"key {1\": 1234"; |
||||
|
check(parse(js, 2, 2, |
||||
|
JSMN_STRING, "key {1", 1, |
||||
|
JSMN_PRIMITIVE, "1234")); |
||||
|
|
||||
|
|
||||
|
#endif |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int test_unmatched_brackets(void) { |
||||
|
const char *js; |
||||
|
js = "\"key 1\": 1234}"; |
||||
|
check(parse(js, JSMN_ERROR_INVAL, 2)); |
||||
|
js = "{\"key 1\": 1234"; |
||||
|
check(parse(js, JSMN_ERROR_PART, 3)); |
||||
|
js = "{\"key 1\": 1234}}"; |
||||
|
check(parse(js, JSMN_ERROR_INVAL, 3)); |
||||
|
js = "\"key 1\"}: 1234"; |
||||
|
check(parse(js, JSMN_ERROR_INVAL, 3)); |
||||
|
js = "{\"key {1\": 1234}"; |
||||
|
check(parse(js, 3, 3, |
||||
|
JSMN_OBJECT, 0, 16, 1, |
||||
|
JSMN_STRING, "key {1", 1, |
||||
|
JSMN_PRIMITIVE, "1234")); |
||||
|
js = "{{\"key 1\": 1234}"; |
||||
|
check(parse(js, JSMN_ERROR_PART, 4)); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int main(void) { |
||||
|
test(test_empty, "test for a empty JSON objects/arrays"); |
||||
|
test(test_object, "test for a JSON objects"); |
||||
|
test(test_array, "test for a JSON arrays"); |
||||
|
test(test_primitive, "test primitive JSON data types"); |
||||
|
test(test_string, "test string JSON data types"); |
||||
|
|
||||
|
test(test_partial_string, "test partial JSON string parsing"); |
||||
|
test(test_partial_array, "test partial array reading"); |
||||
|
test(test_array_nomem, "test array reading with a smaller number of tokens"); |
||||
|
test(test_unquoted_keys, "test unquoted keys (like in JavaScript)"); |
||||
|
test(test_input_length, "test strings that are not null-terminated"); |
||||
|
test(test_issue_22, "test issue #22"); |
||||
|
test(test_issue_27, "test issue #27"); |
||||
|
test(test_count, "test tokens count estimation"); |
||||
|
test(test_nonstrict, "test for non-strict mode"); |
||||
|
test(test_unmatched_brackets, "test for unmatched brackets"); |
||||
|
printf("\nPASSED: %d\nFAILED: %d\n", test_passed, test_failed); |
||||
|
return (test_failed > 0); |
||||
|
} |
@ -0,0 +1,94 @@ |
|||||
|
#ifndef __TEST_UTIL_H__ |
||||
|
#define __TEST_UTIL_H__ |
||||
|
|
||||
|
#include "../jsmn.c" |
||||
|
|
||||
|
static int vtokeq(const char *s, jsmntok_t *t, int numtok, va_list ap) { |
||||
|
if (numtok > 0) { |
||||
|
int i, start, end, size; |
||||
|
int type; |
||||
|
char *value; |
||||
|
|
||||
|
size = -1; |
||||
|
value = NULL; |
||||
|
for (i = 0; i < numtok; i++) { |
||||
|
type = va_arg(ap, int); |
||||
|
if (type == JSMN_STRING) { |
||||
|
value = va_arg(ap, char *); |
||||
|
size = va_arg(ap, int); |
||||
|
start = end = -1; |
||||
|
} else if (type == JSMN_PRIMITIVE) { |
||||
|
value = va_arg(ap, char *); |
||||
|
start = end = size = -1; |
||||
|
} else { |
||||
|
start = va_arg(ap, int); |
||||
|
end = va_arg(ap, int); |
||||
|
size = va_arg(ap, int); |
||||
|
value = NULL; |
||||
|
} |
||||
|
if (t[i].type != type) { |
||||
|
printf("token %d type is %d, not %d\n", i, t[i].type, type); |
||||
|
return 0; |
||||
|
} |
||||
|
if (start != -1 && end != -1) { |
||||
|
if (t[i].start != start) { |
||||
|
printf("token %d start is %d, not %d\n", i, t[i].start, start); |
||||
|
return 0; |
||||
|
} |
||||
|
if (t[i].end != end ) { |
||||
|
printf("token %d end is %d, not %d\n", i, t[i].end, end); |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
if (size != -1 && t[i].size != size) { |
||||
|
printf("token %d size is %d, not %d\n", i, t[i].size, size); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if (s != NULL && value != NULL) { |
||||
|
const char *p = s + t[i].start; |
||||
|
if (strlen(value) != t[i].end - t[i].start || |
||||
|
strncmp(p, value, t[i].end - t[i].start) != 0) { |
||||
|
printf("token %d value is %.*s, not %s\n", i, t[i].end-t[i].start, |
||||
|
s+t[i].start, value); |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
static int tokeq(const char *s, jsmntok_t *tokens, int numtok, ...) { |
||||
|
int ok; |
||||
|
va_list args; |
||||
|
va_start(args, numtok); |
||||
|
ok = vtokeq(s, tokens, numtok, args); |
||||
|
va_end(args); |
||||
|
return ok; |
||||
|
} |
||||
|
|
||||
|
static int parse(const char *s, int status, int numtok, ...) { |
||||
|
int r; |
||||
|
int ok = 1; |
||||
|
va_list args; |
||||
|
jsmn_parser p; |
||||
|
jsmntok_t *t = malloc(numtok * sizeof(jsmntok_t)); |
||||
|
|
||||
|
jsmn_init(&p); |
||||
|
r = jsmn_parse(&p, s, strlen(s), t, numtok); |
||||
|
if (r != status) { |
||||
|
printf("status is %d, not %d\n", r, status); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if (status >= 0) { |
||||
|
va_start(args, numtok); |
||||
|
ok = vtokeq(s, t, numtok, args); |
||||
|
va_end(args); |
||||
|
} |
||||
|
free(t); |
||||
|
return ok; |
||||
|
} |
||||
|
|
||||
|
#endif /* __TEST_UTIL_H__ */ |
@ -0,0 +1,5 @@ |
|||||
|
*~ |
||||
|
*.o |
||||
|
*.lo |
||||
|
*.a |
||||
|
*.la |
@ -0,0 +1,29 @@ |
|||||
|
# Copyright (C) 2012-2016 Free Software Foundation, Inc. |
||||
|
|
||||
|
# Redistribution and use in source and binary forms, with or without |
||||
|
# modification, are permitted provided that the following conditions are |
||||
|
# met: |
||||
|
|
||||
|
# (1) Redistributions of source code must retain the above copyright |
||||
|
# notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
# (2) Redistributions in binary form must reproduce the above copyright |
||||
|
# notice, this list of conditions and the following disclaimer in |
||||
|
# the documentation and/or other materials provided with the |
||||
|
# distribution. |
||||
|
|
||||
|
# (3) The name of the author may not be used to |
||||
|
# endorse or promote products derived from this software without |
||||
|
# specific prior written permission. |
||||
|
|
||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
# POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,206 @@ |
|||||
|
# Makefile.am -- Backtrace Makefile.
|
||||
|
# Copyright (C) 2012-2018 Free Software Foundation, Inc.
|
||||
|
|
||||
|
# Redistribution and use in source and binary forms, with or without
|
||||
|
# modification, are permitted provided that the following conditions are
|
||||
|
# met:
|
||||
|
|
||||
|
# (1) Redistributions of source code must retain the above copyright
|
||||
|
# notice, this list of conditions and the following disclaimer.
|
||||
|
|
||||
|
# (2) Redistributions in binary form must reproduce the above copyright
|
||||
|
# notice, this list of conditions and the following disclaimer in
|
||||
|
# the documentation and/or other materials provided with the
|
||||
|
# distribution.
|
||||
|
|
||||
|
# (3) The name of the author may not be used to
|
||||
|
# endorse or promote products derived from this software without
|
||||
|
# specific prior written permission.
|
||||
|
|
||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
|
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
||||
|
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
||||
|
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
|
# POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
||||
|
ACLOCAL_AMFLAGS = -I config |
||||
|
|
||||
|
AM_CFLAGS = $(EXTRA_FLAGS) $(WARN_FLAGS) $(PIC_FLAG) |
||||
|
|
||||
|
include_HEADERS = backtrace.h backtrace-supported.h |
||||
|
|
||||
|
lib_LTLIBRARIES = libbacktrace.la |
||||
|
|
||||
|
libbacktrace_la_SOURCES = \
|
||||
|
backtrace.h \
|
||||
|
atomic.c \
|
||||
|
dwarf.c \
|
||||
|
fileline.c \
|
||||
|
internal.h \
|
||||
|
posix.c \
|
||||
|
print.c \
|
||||
|
sort.c \
|
||||
|
state.c |
||||
|
|
||||
|
BACKTRACE_FILES = \
|
||||
|
backtrace.c \
|
||||
|
simple.c \
|
||||
|
nounwind.c |
||||
|
|
||||
|
FORMAT_FILES = \
|
||||
|
elf.c \
|
||||
|
pecoff.c \
|
||||
|
unknown.c \
|
||||
|
xcoff.c |
||||
|
|
||||
|
VIEW_FILES = \
|
||||
|
read.c \
|
||||
|
mmapio.c |
||||
|
|
||||
|
ALLOC_FILES = \
|
||||
|
alloc.c \
|
||||
|
mmap.c |
||||
|
|
||||
|
EXTRA_libbacktrace_la_SOURCES = \
|
||||
|
$(BACKTRACE_FILES) \
|
||||
|
$(FORMAT_FILES) \
|
||||
|
$(VIEW_FILES) \
|
||||
|
$(ALLOC_FILES) |
||||
|
|
||||
|
libbacktrace_la_LIBADD = \
|
||||
|
$(BACKTRACE_FILE) \
|
||||
|
$(FORMAT_FILE) \
|
||||
|
$(VIEW_FILE) \
|
||||
|
$(ALLOC_FILE) |
||||
|
|
||||
|
libbacktrace_la_DEPENDENCIES = $(libbacktrace_la_LIBADD) |
||||
|
|
||||
|
# Testsuite.
|
||||
|
|
||||
|
check_PROGRAMS = |
||||
|
CLEANFILES = |
||||
|
|
||||
|
TESTS = $(check_PROGRAMS) |
||||
|
|
||||
|
if NATIVE |
||||
|
|
||||
|
btest_SOURCES = btest.c testlib.c |
||||
|
btest_CFLAGS = $(AM_CFLAGS) -g -O |
||||
|
btest_LDADD = libbacktrace.la |
||||
|
|
||||
|
check_PROGRAMS += btest |
||||
|
|
||||
|
btest_static_SOURCES = btest.c testlib.c |
||||
|
btest_static_CFLAGS = $(AM_CFLAGS) -g -O |
||||
|
btest_static_LDADD = libbacktrace.la |
||||
|
btest_static_LDFLAGS = -static-libtool-libs |
||||
|
|
||||
|
check_PROGRAMS += btest_static |
||||
|
|
||||
|
stest_SOURCES = stest.c |
||||
|
stest_LDADD = libbacktrace.la |
||||
|
|
||||
|
check_PROGRAMS += stest |
||||
|
|
||||
|
ztest_SOURCES = ztest.c testlib.c |
||||
|
ztest_CFLAGS = -DSRCDIR=\"$(srcdir)\" |
||||
|
ztest_LDADD = libbacktrace.la |
||||
|
|
||||
|
if HAVE_ZLIB |
||||
|
ztest_LDADD += -lz |
||||
|
endif |
||||
|
ztest_LDADD += $(CLOCK_GETTIME_LINK) |
||||
|
|
||||
|
check_PROGRAMS += ztest |
||||
|
|
||||
|
edtest_SOURCES = edtest.c edtest2_build.c testlib.c |
||||
|
edtest_LDADD = libbacktrace.la |
||||
|
|
||||
|
check_PROGRAMS += edtest |
||||
|
|
||||
|
edtest2_build.c: gen_edtest2_build; @true |
||||
|
gen_edtest2_build: $(srcdir)/edtest2.c |
||||
|
cat $(srcdir)/edtest2.c > tmp-edtest2_build.c |
||||
|
$(SHELL) $(srcdir)/move-if-change tmp-edtest2_build.c edtest2_build.c |
||||
|
echo timestamp > $@ |
||||
|
|
||||
|
CLEANFILES += edtest2_build.c gen_edtest2_build |
||||
|
|
||||
|
if HAVE_PTHREAD |
||||
|
|
||||
|
check_PROGRAMS += ttest |
||||
|
|
||||
|
ttest_SOURCES = ttest.c testlib.c |
||||
|
ttest_CFLAGS = $(AM_CFLAGS) -pthread |
||||
|
ttest_LDADD = libbacktrace.la |
||||
|
|
||||
|
endif HAVE_PTHREAD |
||||
|
|
||||
|
if HAVE_OBJCOPY_DEBUGLINK |
||||
|
|
||||
|
TESTS += dtest |
||||
|
|
||||
|
dtest: btest_static |
||||
|
$(OBJCOPY) --only-keep-debug btest_static btest.debug |
||||
|
$(OBJCOPY) --strip-debug --add-gnu-debuglink=btest.debug btest_static dtest |
||||
|
|
||||
|
CLEANFILES += dtest btest.debug |
||||
|
|
||||
|
endif HAVE_OBJCOPY_DEBUGLINK |
||||
|
|
||||
|
if HAVE_COMPRESSED_DEBUG |
||||
|
|
||||
|
ctestg_SOURCES = btest.c testlib.c |
||||
|
ctestg_CFLAGS = $(AM_CFLAGS) -g |
||||
|
ctestg_LDFLAGS = -Wl,--compress-debug-sections=zlib-gnu |
||||
|
ctestg_LDADD = libbacktrace.la |
||||
|
|
||||
|
ctesta_SOURCES = btest.c testlib.c |
||||
|
ctesta_CFLAGS = $(AM_CFLAGS) -g |
||||
|
ctesta_LDFLAGS = -Wl,--compress-debug-sections=zlib-gabi |
||||
|
ctesta_LDADD = libbacktrace.la |
||||
|
|
||||
|
check_PROGRAMS += ctestg ctesta |
||||
|
|
||||
|
endif |
||||
|
|
||||
|
endif NATIVE |
||||
|
|
||||
|
# We can't use automake's automatic dependency tracking, because it
|
||||
|
# breaks when using bootstrap-lean. Automatic dependency tracking
|
||||
|
# with GCC bootstrap will cause some of the objects to depend on
|
||||
|
# header files in prev-gcc/include, e.g., stddef.h and stdarg.h. When
|
||||
|
# using bootstrap-lean, prev-gcc is removed after each stage. When
|
||||
|
# running "make install", those header files will be gone, causing the
|
||||
|
# library to be rebuilt at install time. That may not succeed.
|
||||
|
|
||||
|
# These manual dependencies do not include dependencies on unwind.h,
|
||||
|
# even though that is part of GCC, because where to find it depends on
|
||||
|
# whether we are being built as a host library or a target library.
|
||||
|
|
||||
|
alloc.lo: config.h backtrace.h internal.h |
||||
|
backtrace.lo: config.h backtrace.h internal.h |
||||
|
btest.lo: backtrace.h backtrace-supported.h filenames.h |
||||
|
dwarf.lo: config.h filenames.h backtrace.h internal.h |
||||
|
elf.lo: config.h backtrace.h internal.h |
||||
|
fileline.lo: config.h backtrace.h internal.h |
||||
|
mmap.lo: config.h backtrace.h internal.h |
||||
|
mmapio.lo: config.h backtrace.h internal.h |
||||
|
nounwind.lo: config.h internal.h |
||||
|
pecoff.lo: config.h backtrace.h internal.h |
||||
|
posix.lo: config.h backtrace.h internal.h |
||||
|
print.lo: config.h backtrace.h internal.h |
||||
|
read.lo: config.h backtrace.h internal.h |
||||
|
simple.lo: config.h backtrace.h internal.h |
||||
|
sort.lo: config.h backtrace.h internal.h |
||||
|
stest.lo: config.h backtrace.h internal.h |
||||
|
state.lo: config.h backtrace.h backtrace-supported.h internal.h |
||||
|
unknown.lo: config.h backtrace.h internal.h |
||||
|
xcoff.lo: config.h backtrace.h internal.h |
||||
|
|
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,33 @@ |
|||||
|
# libbacktrace |
||||
|
A C library that may be linked into a C/C++ program to produce symbolic backtraces |
||||
|
|
||||
|
Initially written by Ian Lance Taylor <iant@golang.org>. |
||||
|
|
||||
|
This is version 1.0. |
||||
|
It is likely that this will always be version 1.0. |
||||
|
|
||||
|
The libbacktrace library may be linked into a program or library and |
||||
|
used to produce symbolic backtraces. |
||||
|
Sample uses would be to print a detailed backtrace when an error |
||||
|
occurs or to gather detailed profiling information. |
||||
|
|
||||
|
The libbacktrace library is provided under a BSD license. |
||||
|
See the source files for the exact license text. |
||||
|
|
||||
|
The public functions are declared and documented in the header file |
||||
|
backtrace.h, which should be #include'd by a user of the library. |
||||
|
|
||||
|
Building libbacktrace will generate a file backtrace-supported.h, |
||||
|
which a user of the library may use to determine whether backtraces |
||||
|
will work. |
||||
|
See the source file backtrace-supported.h.in for the macros that it |
||||
|
defines. |
||||
|
|
||||
|
As of January 2018, libbacktrace only supports ELF, PE/COFF, and XCOFF |
||||
|
executables with DWARF debugging information. |
||||
|
The library is written to make it straightforward to add support for |
||||
|
other object file and debugging formats. |
||||
|
|
||||
|
The library relies on the C++ unwind API defined at |
||||
|
https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html |
||||
|
This API is provided by GCC. |
@ -0,0 +1,72 @@ |
|||||
|
dnl |
||||
|
dnl Check whether _Unwind_GetIPInfo is available without doing a link |
||||
|
dnl test so we can use this with libstdc++-v3 and libjava. Need to |
||||
|
dnl use $target to set defaults because automatic checking is not possible |
||||
|
dnl without a link test (and maybe even with a link test). |
||||
|
dnl |
||||
|
|
||||
|
AC_DEFUN([GCC_CHECK_UNWIND_GETIPINFO], [ |
||||
|
AC_ARG_WITH(system-libunwind, |
||||
|
[ --with-system-libunwind use installed libunwind]) |
||||
|
# If system-libunwind was not specifically set, pick a default setting. |
||||
|
if test x$with_system_libunwind = x; then |
||||
|
case ${target} in |
||||
|
ia64-*-hpux*) with_system_libunwind=yes ;; |
||||
|
*) with_system_libunwind=no ;; |
||||
|
esac |
||||
|
fi |
||||
|
# Based on system-libunwind and target, do we have ipinfo? |
||||
|
if test x$with_system_libunwind = xyes; then |
||||
|
case ${target} in |
||||
|
ia64-*-*) have_unwind_getipinfo=no ;; |
||||
|
*) have_unwind_getipinfo=yes ;; |
||||
|
esac |
||||
|
else |
||||
|
# Darwin before version 9 does not have _Unwind_GetIPInfo. |
||||
|
changequote(,) |
||||
|
case ${target} in |
||||
|
*-*-darwin[3-8]|*-*-darwin[3-8].*) have_unwind_getipinfo=no ;; |
||||
|
*) have_unwind_getipinfo=yes ;; |
||||
|
esac |
||||
|
changequote([,]) |
||||
|
fi |
||||
|
|
||||
|
if test x$have_unwind_getipinfo = xyes; then |
||||
|
AC_DEFINE(HAVE_GETIPINFO, 1, [Define if _Unwind_GetIPInfo is available.]) |
||||
|
fi |
||||
|
]) |
||||
|
|
||||
|
# ACX_PROG_CC_WARNING_OPTS(WARNINGS, [VARIABLE = WARN_CFLAGS]) |
||||
|
# Sets @VARIABLE@ to the subset of the given options which the |
||||
|
# compiler accepts. |
||||
|
AC_DEFUN([ACX_PROG_CC_WARNING_OPTS], |
||||
|
[AC_REQUIRE([AC_PROG_CC])dnl |
||||
|
AC_LANG_PUSH(C) |
||||
|
m4_pushdef([acx_Var], [m4_default([$2], [WARN_CFLAGS])])dnl |
||||
|
AC_SUBST(acx_Var)dnl |
||||
|
m4_expand_once([acx_Var= |
||||
|
],m4_quote(acx_Var=))dnl |
||||
|
save_CFLAGS="$CFLAGS" |
||||
|
for real_option in $1; do |
||||
|
# Do the check with the no- prefix removed since gcc silently |
||||
|
# accepts any -Wno-* option on purpose |
||||
|
case $real_option in |
||||
|
-Wno-*) option=-W`expr x$real_option : 'x-Wno-\(.*\)'` ;; |
||||
|
*) option=$real_option ;; |
||||
|
esac |
||||
|
AS_VAR_PUSHDEF([acx_Woption], [acx_cv_prog_cc_warning_$option]) |
||||
|
AC_CACHE_CHECK([whether $CC supports $option], acx_Woption, |
||||
|
[CFLAGS="$option" |
||||
|
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], |
||||
|
[AS_VAR_SET(acx_Woption, yes)], |
||||
|
[AS_VAR_SET(acx_Woption, no)]) |
||||
|
]) |
||||
|
AS_IF([test AS_VAR_GET(acx_Woption) = yes], |
||||
|
[acx_Var="$acx_Var${acx_Var:+ }$real_option"]) |
||||
|
AS_VAR_POPDEF([acx_Woption])dnl |
||||
|
done |
||||
|
CFLAGS="$save_CFLAGS" |
||||
|
m4_popdef([acx_Var])dnl |
||||
|
AC_LANG_POP(C) |
||||
|
])# ACX_PROG_CC_WARNING_OPTS |
||||
|
|
@ -0,0 +1,767 @@ |
|||||
|
# generated automatically by aclocal 1.11.6 -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, |
||||
|
# 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, |
||||
|
# Inc. |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without |
||||
|
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A |
||||
|
# PARTICULAR PURPOSE. |
||||
|
|
||||
|
m4_ifndef([AC_AUTOCONF_VERSION], |
||||
|
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl |
||||
|
m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.64],, |
||||
|
[m4_warning([this file was generated for autoconf 2.64. |
||||
|
You have another version of autoconf. It may work, but is not guaranteed to. |
||||
|
If you have problems, you may need to regenerate the build system entirely. |
||||
|
To do so, use the procedure documented by the package, typically `autoreconf'.])]) |
||||
|
|
||||
|
# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008, 2011 Free Software |
||||
|
# Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 1 |
||||
|
|
||||
|
# AM_AUTOMAKE_VERSION(VERSION) |
||||
|
# ---------------------------- |
||||
|
# Automake X.Y traces this macro to ensure aclocal.m4 has been |
||||
|
# generated from the m4 files accompanying Automake X.Y. |
||||
|
# (This private macro should not be called outside this file.) |
||||
|
AC_DEFUN([AM_AUTOMAKE_VERSION], |
||||
|
[am__api_version='1.11' |
||||
|
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to |
||||
|
dnl require some minimum version. Point them to the right macro. |
||||
|
m4_if([$1], [1.11.6], [], |
||||
|
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl |
||||
|
]) |
||||
|
|
||||
|
# _AM_AUTOCONF_VERSION(VERSION) |
||||
|
# ----------------------------- |
||||
|
# aclocal traces this macro to find the Autoconf version. |
||||
|
# This is a private macro too. Using m4_define simplifies |
||||
|
# the logic in aclocal, which can simply ignore this definition. |
||||
|
m4_define([_AM_AUTOCONF_VERSION], []) |
||||
|
|
||||
|
# AM_SET_CURRENT_AUTOMAKE_VERSION |
||||
|
# ------------------------------- |
||||
|
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. |
||||
|
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. |
||||
|
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], |
||||
|
[AM_AUTOMAKE_VERSION([1.11.6])dnl |
||||
|
m4_ifndef([AC_AUTOCONF_VERSION], |
||||
|
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl |
||||
|
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) |
||||
|
|
||||
|
# AM_AUX_DIR_EXPAND -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 1 |
||||
|
|
||||
|
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets |
||||
|
# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to |
||||
|
# `$srcdir', `$srcdir/..', or `$srcdir/../..'. |
||||
|
# |
||||
|
# Of course, Automake must honor this variable whenever it calls a |
||||
|
# tool from the auxiliary directory. The problem is that $srcdir (and |
||||
|
# therefore $ac_aux_dir as well) can be either absolute or relative, |
||||
|
# depending on how configure is run. This is pretty annoying, since |
||||
|
# it makes $ac_aux_dir quite unusable in subdirectories: in the top |
||||
|
# source directory, any form will work fine, but in subdirectories a |
||||
|
# relative path needs to be adjusted first. |
||||
|
# |
||||
|
# $ac_aux_dir/missing |
||||
|
# fails when called from a subdirectory if $ac_aux_dir is relative |
||||
|
# $top_srcdir/$ac_aux_dir/missing |
||||
|
# fails if $ac_aux_dir is absolute, |
||||
|
# fails when called from a subdirectory in a VPATH build with |
||||
|
# a relative $ac_aux_dir |
||||
|
# |
||||
|
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir |
||||
|
# are both prefixed by $srcdir. In an in-source build this is usually |
||||
|
# harmless because $srcdir is `.', but things will broke when you |
||||
|
# start a VPATH build or use an absolute $srcdir. |
||||
|
# |
||||
|
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, |
||||
|
# iff we strip the leading $srcdir from $ac_aux_dir. That would be: |
||||
|
# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` |
||||
|
# and then we would define $MISSING as |
||||
|
# MISSING="\${SHELL} $am_aux_dir/missing" |
||||
|
# This will work as long as MISSING is not called from configure, because |
||||
|
# unfortunately $(top_srcdir) has no meaning in configure. |
||||
|
# However there are other variables, like CC, which are often used in |
||||
|
# configure, and could therefore not use this "fixed" $ac_aux_dir. |
||||
|
# |
||||
|
# Another solution, used here, is to always expand $ac_aux_dir to an |
||||
|
# absolute PATH. The drawback is that using absolute paths prevent a |
||||
|
# configured tree to be moved without reconfiguration. |
||||
|
|
||||
|
AC_DEFUN([AM_AUX_DIR_EXPAND], |
||||
|
[dnl Rely on autoconf to set up CDPATH properly. |
||||
|
AC_PREREQ([2.50])dnl |
||||
|
# expand $ac_aux_dir to an absolute path |
||||
|
am_aux_dir=`cd $ac_aux_dir && pwd` |
||||
|
]) |
||||
|
|
||||
|
# AM_CONDITIONAL -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 |
||||
|
# Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 9 |
||||
|
|
||||
|
# AM_CONDITIONAL(NAME, SHELL-CONDITION) |
||||
|
# ------------------------------------- |
||||
|
# Define a conditional. |
||||
|
AC_DEFUN([AM_CONDITIONAL], |
||||
|
[AC_PREREQ(2.52)dnl |
||||
|
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], |
||||
|
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl |
||||
|
AC_SUBST([$1_TRUE])dnl |
||||
|
AC_SUBST([$1_FALSE])dnl |
||||
|
_AM_SUBST_NOTMAKE([$1_TRUE])dnl |
||||
|
_AM_SUBST_NOTMAKE([$1_FALSE])dnl |
||||
|
m4_define([_AM_COND_VALUE_$1], [$2])dnl |
||||
|
if $2; then |
||||
|
$1_TRUE= |
||||
|
$1_FALSE='#' |
||||
|
else |
||||
|
$1_TRUE='#' |
||||
|
$1_FALSE= |
||||
|
fi |
||||
|
AC_CONFIG_COMMANDS_PRE( |
||||
|
[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then |
||||
|
AC_MSG_ERROR([[conditional "$1" was never defined. |
||||
|
Usually this means the macro was only invoked conditionally.]]) |
||||
|
fi])]) |
||||
|
|
||||
|
# Do all the work for Automake. -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, |
||||
|
# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 16 |
||||
|
|
||||
|
# This macro actually does too much. Some checks are only needed if |
||||
|
# your package does certain things. But this isn't really a big deal. |
||||
|
|
||||
|
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) |
||||
|
# AM_INIT_AUTOMAKE([OPTIONS]) |
||||
|
# ----------------------------------------------- |
||||
|
# The call with PACKAGE and VERSION arguments is the old style |
||||
|
# call (pre autoconf-2.50), which is being phased out. PACKAGE |
||||
|
# and VERSION should now be passed to AC_INIT and removed from |
||||
|
# the call to AM_INIT_AUTOMAKE. |
||||
|
# We support both call styles for the transition. After |
||||
|
# the next Automake release, Autoconf can make the AC_INIT |
||||
|
# arguments mandatory, and then we can depend on a new Autoconf |
||||
|
# release and drop the old call support. |
||||
|
AC_DEFUN([AM_INIT_AUTOMAKE], |
||||
|
[AC_PREREQ([2.62])dnl |
||||
|
dnl Autoconf wants to disallow AM_ names. We explicitly allow |
||||
|
dnl the ones we care about. |
||||
|
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl |
||||
|
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl |
||||
|
AC_REQUIRE([AC_PROG_INSTALL])dnl |
||||
|
if test "`cd $srcdir && pwd`" != "`pwd`"; then |
||||
|
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output |
||||
|
# is not polluted with repeated "-I." |
||||
|
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl |
||||
|
# test to see if srcdir already configured |
||||
|
if test -f $srcdir/config.status; then |
||||
|
AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# test whether we have cygpath |
||||
|
if test -z "$CYGPATH_W"; then |
||||
|
if (cygpath --version) >/dev/null 2>/dev/null; then |
||||
|
CYGPATH_W='cygpath -w' |
||||
|
else |
||||
|
CYGPATH_W=echo |
||||
|
fi |
||||
|
fi |
||||
|
AC_SUBST([CYGPATH_W]) |
||||
|
|
||||
|
# Define the identity of the package. |
||||
|
dnl Distinguish between old-style and new-style calls. |
||||
|
m4_ifval([$2], |
||||
|
[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl |
||||
|
AC_SUBST([PACKAGE], [$1])dnl |
||||
|
AC_SUBST([VERSION], [$2])], |
||||
|
[_AM_SET_OPTIONS([$1])dnl |
||||
|
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. |
||||
|
m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, |
||||
|
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl |
||||
|
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl |
||||
|
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl |
||||
|
|
||||
|
_AM_IF_OPTION([no-define],, |
||||
|
[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package]) |
||||
|
AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl |
||||
|
|
||||
|
# Some tools Automake needs. |
||||
|
AC_REQUIRE([AM_SANITY_CHECK])dnl |
||||
|
AC_REQUIRE([AC_ARG_PROGRAM])dnl |
||||
|
AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version}) |
||||
|
AM_MISSING_PROG(AUTOCONF, autoconf) |
||||
|
AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) |
||||
|
AM_MISSING_PROG(AUTOHEADER, autoheader) |
||||
|
AM_MISSING_PROG(MAKEINFO, makeinfo) |
||||
|
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl |
||||
|
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl |
||||
|
AC_REQUIRE([AM_PROG_MKDIR_P])dnl |
||||
|
# We need awk for the "check" target. The system "awk" is bad on |
||||
|
# some platforms. |
||||
|
AC_REQUIRE([AC_PROG_AWK])dnl |
||||
|
AC_REQUIRE([AC_PROG_MAKE_SET])dnl |
||||
|
AC_REQUIRE([AM_SET_LEADING_DOT])dnl |
||||
|
_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], |
||||
|
[_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], |
||||
|
[_AM_PROG_TAR([v7])])]) |
||||
|
_AM_IF_OPTION([no-dependencies],, |
||||
|
[AC_PROVIDE_IFELSE([AC_PROG_CC], |
||||
|
[_AM_DEPENDENCIES(CC)], |
||||
|
[define([AC_PROG_CC], |
||||
|
defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl |
||||
|
AC_PROVIDE_IFELSE([AC_PROG_CXX], |
||||
|
[_AM_DEPENDENCIES(CXX)], |
||||
|
[define([AC_PROG_CXX], |
||||
|
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl |
||||
|
AC_PROVIDE_IFELSE([AC_PROG_OBJC], |
||||
|
[_AM_DEPENDENCIES(OBJC)], |
||||
|
[define([AC_PROG_OBJC], |
||||
|
defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl |
||||
|
]) |
||||
|
_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl |
||||
|
dnl The `parallel-tests' driver may need to know about EXEEXT, so add the |
||||
|
dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro |
||||
|
dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. |
||||
|
AC_CONFIG_COMMANDS_PRE(dnl |
||||
|
[m4_provide_if([_AM_COMPILER_EXEEXT], |
||||
|
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl |
||||
|
]) |
||||
|
|
||||
|
dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not |
||||
|
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further |
||||
|
dnl mangled by Autoconf and run in a shell conditional statement. |
||||
|
m4_define([_AC_COMPILER_EXEEXT], |
||||
|
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) |
||||
|
|
||||
|
|
||||
|
# When config.status generates a header, we must update the stamp-h file. |
||||
|
# This file resides in the same directory as the config header |
||||
|
# that is generated. The stamp files are numbered to have different names. |
||||
|
|
||||
|
# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the |
||||
|
# loop where config.status creates the headers, so we can generate |
||||
|
# our stamp files there. |
||||
|
AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], |
||||
|
[# Compute $1's index in $config_headers. |
||||
|
_am_arg=$1 |
||||
|
_am_stamp_count=1 |
||||
|
for _am_header in $config_headers :; do |
||||
|
case $_am_header in |
||||
|
$_am_arg | $_am_arg:* ) |
||||
|
break ;; |
||||
|
* ) |
||||
|
_am_stamp_count=`expr $_am_stamp_count + 1` ;; |
||||
|
esac |
||||
|
done |
||||
|
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) |
||||
|
|
||||
|
# Copyright (C) 2001, 2003, 2005, 2008, 2011 Free Software Foundation, |
||||
|
# Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 1 |
||||
|
|
||||
|
# AM_PROG_INSTALL_SH |
||||
|
# ------------------ |
||||
|
# Define $install_sh. |
||||
|
AC_DEFUN([AM_PROG_INSTALL_SH], |
||||
|
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl |
||||
|
if test x"${install_sh}" != xset; then |
||||
|
case $am_aux_dir in |
||||
|
*\ * | *\ *) |
||||
|
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; |
||||
|
*) |
||||
|
install_sh="\${SHELL} $am_aux_dir/install-sh" |
||||
|
esac |
||||
|
fi |
||||
|
AC_SUBST(install_sh)]) |
||||
|
|
||||
|
# Copyright (C) 2003, 2005 Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 2 |
||||
|
|
||||
|
# Check whether the underlying file-system supports filenames |
||||
|
# with a leading dot. For instance MS-DOS doesn't. |
||||
|
AC_DEFUN([AM_SET_LEADING_DOT], |
||||
|
[rm -rf .tst 2>/dev/null |
||||
|
mkdir .tst 2>/dev/null |
||||
|
if test -d .tst; then |
||||
|
am__leading_dot=. |
||||
|
else |
||||
|
am__leading_dot=_ |
||||
|
fi |
||||
|
rmdir .tst 2>/dev/null |
||||
|
AC_SUBST([am__leading_dot])]) |
||||
|
|
||||
|
# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- |
||||
|
# From Jim Meyering |
||||
|
|
||||
|
# Copyright (C) 1996, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2008, |
||||
|
# 2011 Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 5 |
||||
|
|
||||
|
# AM_MAINTAINER_MODE([DEFAULT-MODE]) |
||||
|
# ---------------------------------- |
||||
|
# Control maintainer-specific portions of Makefiles. |
||||
|
# Default is to disable them, unless `enable' is passed literally. |
||||
|
# For symmetry, `disable' may be passed as well. Anyway, the user |
||||
|
# can override the default with the --enable/--disable switch. |
||||
|
AC_DEFUN([AM_MAINTAINER_MODE], |
||||
|
[m4_case(m4_default([$1], [disable]), |
||||
|
[enable], [m4_define([am_maintainer_other], [disable])], |
||||
|
[disable], [m4_define([am_maintainer_other], [enable])], |
||||
|
[m4_define([am_maintainer_other], [enable]) |
||||
|
m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) |
||||
|
AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) |
||||
|
dnl maintainer-mode's default is 'disable' unless 'enable' is passed |
||||
|
AC_ARG_ENABLE([maintainer-mode], |
||||
|
[ --][am_maintainer_other][-maintainer-mode am_maintainer_other make rules and dependencies not useful |
||||
|
(and sometimes confusing) to the casual installer], |
||||
|
[USE_MAINTAINER_MODE=$enableval], |
||||
|
[USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) |
||||
|
AC_MSG_RESULT([$USE_MAINTAINER_MODE]) |
||||
|
AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) |
||||
|
MAINT=$MAINTAINER_MODE_TRUE |
||||
|
AC_SUBST([MAINT])dnl |
||||
|
] |
||||
|
) |
||||
|
|
||||
|
AU_DEFUN([jm_MAINTAINER_MODE], [AM_MAINTAINER_MODE]) |
||||
|
|
||||
|
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 |
||||
|
# Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 6 |
||||
|
|
||||
|
# AM_MISSING_PROG(NAME, PROGRAM) |
||||
|
# ------------------------------ |
||||
|
AC_DEFUN([AM_MISSING_PROG], |
||||
|
[AC_REQUIRE([AM_MISSING_HAS_RUN]) |
||||
|
$1=${$1-"${am_missing_run}$2"} |
||||
|
AC_SUBST($1)]) |
||||
|
|
||||
|
|
||||
|
# AM_MISSING_HAS_RUN |
||||
|
# ------------------ |
||||
|
# Define MISSING if not defined so far and test if it supports --run. |
||||
|
# If it does, set am_missing_run to use it, otherwise, to nothing. |
||||
|
AC_DEFUN([AM_MISSING_HAS_RUN], |
||||
|
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl |
||||
|
AC_REQUIRE_AUX_FILE([missing])dnl |
||||
|
if test x"${MISSING+set}" != xset; then |
||||
|
case $am_aux_dir in |
||||
|
*\ * | *\ *) |
||||
|
MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; |
||||
|
*) |
||||
|
MISSING="\${SHELL} $am_aux_dir/missing" ;; |
||||
|
esac |
||||
|
fi |
||||
|
# Use eval to expand $SHELL |
||||
|
if eval "$MISSING --run true"; then |
||||
|
am_missing_run="$MISSING --run " |
||||
|
else |
||||
|
am_missing_run= |
||||
|
AC_MSG_WARN([`missing' script is too old or missing]) |
||||
|
fi |
||||
|
]) |
||||
|
|
||||
|
# Copyright (C) 2003, 2004, 2005, 2006, 2011 Free Software Foundation, |
||||
|
# Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 1 |
||||
|
|
||||
|
# AM_PROG_MKDIR_P |
||||
|
# --------------- |
||||
|
# Check for `mkdir -p'. |
||||
|
AC_DEFUN([AM_PROG_MKDIR_P], |
||||
|
[AC_PREREQ([2.60])dnl |
||||
|
AC_REQUIRE([AC_PROG_MKDIR_P])dnl |
||||
|
dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, |
||||
|
dnl while keeping a definition of mkdir_p for backward compatibility. |
||||
|
dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. |
||||
|
dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of |
||||
|
dnl Makefile.ins that do not define MKDIR_P, so we do our own |
||||
|
dnl adjustment using top_builddir (which is defined more often than |
||||
|
dnl MKDIR_P). |
||||
|
AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl |
||||
|
case $mkdir_p in |
||||
|
[[\\/$]]* | ?:[[\\/]]*) ;; |
||||
|
*/*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; |
||||
|
esac |
||||
|
]) |
||||
|
|
||||
|
# Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2012 |
||||
|
# Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 6 |
||||
|
|
||||
|
# AM_ENABLE_MULTILIB([MAKEFILE], [REL-TO-TOP-SRCDIR]) |
||||
|
# --------------------------------------------------- |
||||
|
# Add --enable-multilib to configure. |
||||
|
AC_DEFUN([AM_ENABLE_MULTILIB], |
||||
|
[m4_warn([obsolete], [$0 will be removed from Automake core soon. |
||||
|
Files implementing the "multilib" feature are (and will remain) available |
||||
|
to the 'contrib/' directory in the Automake distribution.])]dnl |
||||
|
[# Default to --enable-multilib |
||||
|
AC_ARG_ENABLE(multilib, |
||||
|
[ --enable-multilib build many library versions (default)], |
||||
|
[case "$enableval" in |
||||
|
yes) multilib=yes ;; |
||||
|
no) multilib=no ;; |
||||
|
*) AC_MSG_ERROR([bad value $enableval for multilib option]) ;; |
||||
|
esac], |
||||
|
[multilib=yes]) |
||||
|
|
||||
|
# We may get other options which we leave undocumented: |
||||
|
# --with-target-subdir, --with-multisrctop, --with-multisubdir |
||||
|
# See config-ml.in if you want the gory details. |
||||
|
|
||||
|
if test "$srcdir" = "."; then |
||||
|
if test "$with_target_subdir" != "."; then |
||||
|
multi_basedir="$srcdir/$with_multisrctop../$2" |
||||
|
else |
||||
|
multi_basedir="$srcdir/$with_multisrctop$2" |
||||
|
fi |
||||
|
else |
||||
|
multi_basedir="$srcdir/$2" |
||||
|
fi |
||||
|
AC_SUBST(multi_basedir) |
||||
|
|
||||
|
# Even if the default multilib is not a cross compilation, |
||||
|
# it may be that some of the other multilibs are. |
||||
|
if test $cross_compiling = no && test $multilib = yes \ |
||||
|
&& test "x${with_multisubdir}" != x ; then |
||||
|
cross_compiling=maybe |
||||
|
fi |
||||
|
|
||||
|
AC_OUTPUT_COMMANDS([ |
||||
|
# Only add multilib support code if we just rebuilt the top-level |
||||
|
# Makefile. |
||||
|
case " $CONFIG_FILES " in |
||||
|
*" ]m4_default([$1],Makefile)[ "*) |
||||
|
ac_file=]m4_default([$1],Makefile)[ . ${multi_basedir}/config-ml.in |
||||
|
;; |
||||
|
esac], |
||||
|
[ |
||||
|
srcdir="$srcdir" |
||||
|
host="$host" |
||||
|
target="$target" |
||||
|
with_multisubdir="$with_multisubdir" |
||||
|
with_multisrctop="$with_multisrctop" |
||||
|
with_target_subdir="$with_target_subdir" |
||||
|
ac_configure_args="${multilib_arg} ${ac_configure_args}" |
||||
|
multi_basedir="$multi_basedir" |
||||
|
CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} |
||||
|
CC="$CC"])])dnl |
||||
|
|
||||
|
# Helper functions for option handling. -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 2001, 2002, 2003, 2005, 2008, 2010 Free Software |
||||
|
# Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 5 |
||||
|
|
||||
|
# _AM_MANGLE_OPTION(NAME) |
||||
|
# ----------------------- |
||||
|
AC_DEFUN([_AM_MANGLE_OPTION], |
||||
|
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) |
||||
|
|
||||
|
# _AM_SET_OPTION(NAME) |
||||
|
# -------------------- |
||||
|
# Set option NAME. Presently that only means defining a flag for this option. |
||||
|
AC_DEFUN([_AM_SET_OPTION], |
||||
|
[m4_define(_AM_MANGLE_OPTION([$1]), 1)]) |
||||
|
|
||||
|
# _AM_SET_OPTIONS(OPTIONS) |
||||
|
# ------------------------ |
||||
|
# OPTIONS is a space-separated list of Automake options. |
||||
|
AC_DEFUN([_AM_SET_OPTIONS], |
||||
|
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) |
||||
|
|
||||
|
# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) |
||||
|
# ------------------------------------------- |
||||
|
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. |
||||
|
AC_DEFUN([_AM_IF_OPTION], |
||||
|
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) |
||||
|
|
||||
|
# Check to make sure that the build environment is sane. -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 |
||||
|
# Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 5 |
||||
|
|
||||
|
# AM_SANITY_CHECK |
||||
|
# --------------- |
||||
|
AC_DEFUN([AM_SANITY_CHECK], |
||||
|
[AC_MSG_CHECKING([whether build environment is sane]) |
||||
|
# Just in case |
||||
|
sleep 1 |
||||
|
echo timestamp > conftest.file |
||||
|
# Reject unsafe characters in $srcdir or the absolute working directory |
||||
|
# name. Accept space and tab only in the latter. |
||||
|
am_lf=' |
||||
|
' |
||||
|
case `pwd` in |
||||
|
*[[\\\"\#\$\&\'\`$am_lf]]*) |
||||
|
AC_MSG_ERROR([unsafe absolute working directory name]);; |
||||
|
esac |
||||
|
case $srcdir in |
||||
|
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) |
||||
|
AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; |
||||
|
esac |
||||
|
|
||||
|
# Do `set' in a subshell so we don't clobber the current shell's |
||||
|
# arguments. Must try -L first in case configure is actually a |
||||
|
# symlink; some systems play weird games with the mod time of symlinks |
||||
|
# (eg FreeBSD returns the mod time of the symlink's containing |
||||
|
# directory). |
||||
|
if ( |
||||
|
set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` |
||||
|
if test "$[*]" = "X"; then |
||||
|
# -L didn't work. |
||||
|
set X `ls -t "$srcdir/configure" conftest.file` |
||||
|
fi |
||||
|
rm -f conftest.file |
||||
|
if test "$[*]" != "X $srcdir/configure conftest.file" \ |
||||
|
&& test "$[*]" != "X conftest.file $srcdir/configure"; then |
||||
|
|
||||
|
# If neither matched, then we have a broken ls. This can happen |
||||
|
# if, for instance, CONFIG_SHELL is bash and it inherits a |
||||
|
# broken ls alias from the environment. This has actually |
||||
|
# happened. Such a system could not be considered "sane". |
||||
|
AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken |
||||
|
alias in your environment]) |
||||
|
fi |
||||
|
|
||||
|
test "$[2]" = conftest.file |
||||
|
) |
||||
|
then |
||||
|
# Ok. |
||||
|
: |
||||
|
else |
||||
|
AC_MSG_ERROR([newly created file is older than distributed files! |
||||
|
Check your system clock]) |
||||
|
fi |
||||
|
AC_MSG_RESULT(yes)]) |
||||
|
|
||||
|
# Copyright (C) 2001, 2003, 2005, 2011 Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 1 |
||||
|
|
||||
|
# AM_PROG_INSTALL_STRIP |
||||
|
# --------------------- |
||||
|
# One issue with vendor `install' (even GNU) is that you can't |
||||
|
# specify the program used to strip binaries. This is especially |
||||
|
# annoying in cross-compiling environments, where the build's strip |
||||
|
# is unlikely to handle the host's binaries. |
||||
|
# Fortunately install-sh will honor a STRIPPROG variable, so we |
||||
|
# always use install-sh in `make install-strip', and initialize |
||||
|
# STRIPPROG with the value of the STRIP variable (set by the user). |
||||
|
AC_DEFUN([AM_PROG_INSTALL_STRIP], |
||||
|
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl |
||||
|
# Installed binaries are usually stripped using `strip' when the user |
||||
|
# run `make install-strip'. However `strip' might not be the right |
||||
|
# tool to use in cross-compilation environments, therefore Automake |
||||
|
# will honor the `STRIP' environment variable to overrule this program. |
||||
|
dnl Don't test for $cross_compiling = yes, because it might be `maybe'. |
||||
|
if test "$cross_compiling" != no; then |
||||
|
AC_CHECK_TOOL([STRIP], [strip], :) |
||||
|
fi |
||||
|
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" |
||||
|
AC_SUBST([INSTALL_STRIP_PROGRAM])]) |
||||
|
|
||||
|
# Copyright (C) 2006, 2008, 2010 Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 3 |
||||
|
|
||||
|
# _AM_SUBST_NOTMAKE(VARIABLE) |
||||
|
# --------------------------- |
||||
|
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. |
||||
|
# This macro is traced by Automake. |
||||
|
AC_DEFUN([_AM_SUBST_NOTMAKE]) |
||||
|
|
||||
|
# AM_SUBST_NOTMAKE(VARIABLE) |
||||
|
# -------------------------- |
||||
|
# Public sister of _AM_SUBST_NOTMAKE. |
||||
|
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) |
||||
|
|
||||
|
# Check how to create a tarball. -*- Autoconf -*- |
||||
|
|
||||
|
# Copyright (C) 2004, 2005, 2012 Free Software Foundation, Inc. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation |
||||
|
# gives unlimited permission to copy and/or distribute it, |
||||
|
# with or without modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 2 |
||||
|
|
||||
|
# _AM_PROG_TAR(FORMAT) |
||||
|
# -------------------- |
||||
|
# Check how to create a tarball in format FORMAT. |
||||
|
# FORMAT should be one of `v7', `ustar', or `pax'. |
||||
|
# |
||||
|
# Substitute a variable $(am__tar) that is a command |
||||
|
# writing to stdout a FORMAT-tarball containing the directory |
||||
|
# $tardir. |
||||
|
# tardir=directory && $(am__tar) > result.tar |
||||
|
# |
||||
|
# Substitute a variable $(am__untar) that extract such |
||||
|
# a tarball read from stdin. |
||||
|
# $(am__untar) < result.tar |
||||
|
AC_DEFUN([_AM_PROG_TAR], |
||||
|
[# Always define AMTAR for backward compatibility. Yes, it's still used |
||||
|
# in the wild :-( We should find a proper way to deprecate it ... |
||||
|
AC_SUBST([AMTAR], ['$${TAR-tar}']) |
||||
|
m4_if([$1], [v7], |
||||
|
[am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], |
||||
|
[m4_case([$1], [ustar],, [pax],, |
||||
|
[m4_fatal([Unknown tar format])]) |
||||
|
AC_MSG_CHECKING([how to create a $1 tar archive]) |
||||
|
# Loop over all known methods to create a tar archive until one works. |
||||
|
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' |
||||
|
_am_tools=${am_cv_prog_tar_$1-$_am_tools} |
||||
|
# Do not fold the above two line into one, because Tru64 sh and |
||||
|
# Solaris sh will not grok spaces in the rhs of `-'. |
||||
|
for _am_tool in $_am_tools |
||||
|
do |
||||
|
case $_am_tool in |
||||
|
gnutar) |
||||
|
for _am_tar in tar gnutar gtar; |
||||
|
do |
||||
|
AM_RUN_LOG([$_am_tar --version]) && break |
||||
|
done |
||||
|
am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' |
||||
|
am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' |
||||
|
am__untar="$_am_tar -xf -" |
||||
|
;; |
||||
|
plaintar) |
||||
|
# Must skip GNU tar: if it does not support --format= it doesn't create |
||||
|
# ustar tarball either. |
||||
|
(tar --version) >/dev/null 2>&1 && continue |
||||
|
am__tar='tar chf - "$$tardir"' |
||||
|
am__tar_='tar chf - "$tardir"' |
||||
|
am__untar='tar xf -' |
||||
|
;; |
||||
|
pax) |
||||
|
am__tar='pax -L -x $1 -w "$$tardir"' |
||||
|
am__tar_='pax -L -x $1 -w "$tardir"' |
||||
|
am__untar='pax -r' |
||||
|
;; |
||||
|
cpio) |
||||
|
am__tar='find "$$tardir" -print | cpio -o -H $1 -L' |
||||
|
am__tar_='find "$tardir" -print | cpio -o -H $1 -L' |
||||
|
am__untar='cpio -i -H $1 -d' |
||||
|
;; |
||||
|
none) |
||||
|
am__tar=false |
||||
|
am__tar_=false |
||||
|
am__untar=false |
||||
|
;; |
||||
|
esac |
||||
|
|
||||
|
# If the value was cached, stop now. We just wanted to have am__tar |
||||
|
# and am__untar set. |
||||
|
test -n "${am_cv_prog_tar_$1}" && break |
||||
|
|
||||
|
# tar/untar a dummy directory, and stop if the command works |
||||
|
rm -rf conftest.dir |
||||
|
mkdir conftest.dir |
||||
|
echo GrepMe > conftest.dir/file |
||||
|
AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) |
||||
|
rm -rf conftest.dir |
||||
|
if test -s conftest.tar; then |
||||
|
AM_RUN_LOG([$am__untar <conftest.tar]) |
||||
|
grep GrepMe conftest.dir/file >/dev/null 2>&1 && break |
||||
|
fi |
||||
|
done |
||||
|
rm -rf conftest.dir |
||||
|
|
||||
|
AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) |
||||
|
AC_MSG_RESULT([$am_cv_prog_tar_$1])]) |
||||
|
AC_SUBST([am__tar]) |
||||
|
AC_SUBST([am__untar]) |
||||
|
]) # _AM_PROG_TAR |
||||
|
|
||||
|
m4_include([config/libtool.m4]) |
||||
|
m4_include([config/ltoptions.m4]) |
||||
|
m4_include([config/ltsugar.m4]) |
||||
|
m4_include([config/ltversion.m4]) |
||||
|
m4_include([config/lt~obsolete.m4]) |
||||
|
m4_include([acinclude.m4]) |
@ -0,0 +1,156 @@ |
|||||
|
/* alloc.c -- Memory allocation without mmap.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* Allocation routines to use on systems that do not support anonymous
|
||||
|
mmap. This implementation just uses malloc, which means that the |
||||
|
backtrace functions may not be safely invoked from a signal |
||||
|
handler. */ |
||||
|
|
||||
|
/* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't
|
||||
|
report an error. */ |
||||
|
|
||||
|
void * |
||||
|
backtrace_alloc (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
size_t size, backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
void *ret; |
||||
|
|
||||
|
ret = malloc (size); |
||||
|
if (ret == NULL) |
||||
|
{ |
||||
|
if (error_callback) |
||||
|
error_callback (data, "malloc", errno); |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/* Free memory. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_free (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
void *p, size_t size ATTRIBUTE_UNUSED, |
||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
||||
|
void *data ATTRIBUTE_UNUSED) |
||||
|
{ |
||||
|
free (p); |
||||
|
} |
||||
|
|
||||
|
/* Grow VEC by SIZE bytes. */ |
||||
|
|
||||
|
void * |
||||
|
backtrace_vector_grow (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
size_t size, backtrace_error_callback error_callback, |
||||
|
void *data, struct backtrace_vector *vec) |
||||
|
{ |
||||
|
void *ret; |
||||
|
|
||||
|
if (size > vec->alc) |
||||
|
{ |
||||
|
size_t alc; |
||||
|
void *base; |
||||
|
|
||||
|
if (vec->size == 0) |
||||
|
alc = 32 * size; |
||||
|
else if (vec->size >= 4096) |
||||
|
alc = vec->size + 4096; |
||||
|
else |
||||
|
alc = 2 * vec->size; |
||||
|
|
||||
|
if (alc < vec->size + size) |
||||
|
alc = vec->size + size; |
||||
|
|
||||
|
base = realloc (vec->base, alc); |
||||
|
if (base == NULL) |
||||
|
{ |
||||
|
error_callback (data, "realloc", errno); |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
vec->base = base; |
||||
|
vec->alc = alc - vec->size; |
||||
|
} |
||||
|
|
||||
|
ret = (char *) vec->base + vec->size; |
||||
|
vec->size += size; |
||||
|
vec->alc -= size; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/* Finish the current allocation on VEC. */ |
||||
|
|
||||
|
void * |
||||
|
backtrace_vector_finish (struct backtrace_state *state, |
||||
|
struct backtrace_vector *vec, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
void *ret; |
||||
|
|
||||
|
/* With this allocator we call realloc in backtrace_vector_grow,
|
||||
|
which means we can't easily reuse the memory here. So just |
||||
|
release it. */ |
||||
|
if (!backtrace_vector_release (state, vec, error_callback, data)) |
||||
|
return NULL; |
||||
|
ret = vec->base; |
||||
|
vec->base = NULL; |
||||
|
vec->size = 0; |
||||
|
vec->alc = 0; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/* Release any extra space allocated for VEC. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_vector_release (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
struct backtrace_vector *vec, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
vec->base = realloc (vec->base, vec->size); |
||||
|
if (vec->base == NULL) |
||||
|
{ |
||||
|
error_callback (data, "realloc", errno); |
||||
|
return 0; |
||||
|
} |
||||
|
vec->alc = 0; |
||||
|
return 1; |
||||
|
} |
@ -0,0 +1,113 @@ |
|||||
|
/* atomic.c -- Support for atomic functions if not present.
|
||||
|
Copyright (C) 2013-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "backtrace-supported.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* This file holds implementations of the atomic functions that are
|
||||
|
used if the host compiler has the sync functions but not the atomic |
||||
|
functions, as is true of versions of GCC before 4.7. */ |
||||
|
|
||||
|
#if !defined (HAVE_ATOMIC_FUNCTIONS) && defined (HAVE_SYNC_FUNCTIONS) |
||||
|
|
||||
|
/* Do an atomic load of a pointer. */ |
||||
|
|
||||
|
void * |
||||
|
backtrace_atomic_load_pointer (void *arg) |
||||
|
{ |
||||
|
void **pp; |
||||
|
void *p; |
||||
|
|
||||
|
pp = (void **) arg; |
||||
|
p = *pp; |
||||
|
while (!__sync_bool_compare_and_swap (pp, p, p)) |
||||
|
p = *pp; |
||||
|
return p; |
||||
|
} |
||||
|
|
||||
|
/* Do an atomic load of an int. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_atomic_load_int (int *p) |
||||
|
{ |
||||
|
int i; |
||||
|
|
||||
|
i = *p; |
||||
|
while (!__sync_bool_compare_and_swap (p, i, i)) |
||||
|
i = *p; |
||||
|
return i; |
||||
|
} |
||||
|
|
||||
|
/* Do an atomic store of a pointer. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_atomic_store_pointer (void *arg, void *p) |
||||
|
{ |
||||
|
void **pp; |
||||
|
void *old; |
||||
|
|
||||
|
pp = (void **) arg; |
||||
|
old = *pp; |
||||
|
while (!__sync_bool_compare_and_swap (pp, old, p)) |
||||
|
old = *pp; |
||||
|
} |
||||
|
|
||||
|
/* Do an atomic store of a size_t value. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_atomic_store_size_t (size_t *p, size_t v) |
||||
|
{ |
||||
|
size_t old; |
||||
|
|
||||
|
old = *p; |
||||
|
while (!__sync_bool_compare_and_swap (p, old, v)) |
||||
|
old = *p; |
||||
|
} |
||||
|
|
||||
|
/* Do an atomic store of a int value. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_atomic_store_int (int *p, int v) |
||||
|
{ |
||||
|
size_t old; |
||||
|
|
||||
|
old = *p; |
||||
|
while (!__sync_bool_compare_and_swap (p, old, v)) |
||||
|
old = *p; |
||||
|
} |
||||
|
|
||||
|
#endif |
@ -0,0 +1,66 @@ |
|||||
|
/* backtrace-supported.h.in -- Whether stack backtrace is supported.
|
||||
|
Copyright (C) 2012-2016 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
/* The file backtrace-supported.h.in is used by configure to generate
|
||||
|
the file backtrace-supported.h. The file backtrace-supported.h may |
||||
|
be #include'd to see whether the backtrace library will be able to |
||||
|
get a backtrace and produce symbolic information. */ |
||||
|
|
||||
|
|
||||
|
/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library
|
||||
|
should work, 0 if it will not. Libraries may #include this to make |
||||
|
other arrangements. */ |
||||
|
|
||||
|
#define BACKTRACE_SUPPORTED @BACKTRACE_SUPPORTED@ |
||||
|
|
||||
|
/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace
|
||||
|
library will call malloc as it works, 0 if it will call mmap |
||||
|
instead. This may be used to determine whether it is safe to call |
||||
|
the backtrace functions from a signal handler. In general this |
||||
|
only applies to calls like backtrace and backtrace_pcinfo. It does |
||||
|
not apply to backtrace_simple, which never calls malloc. It does |
||||
|
not apply to backtrace_print, which always calls fprintf and |
||||
|
therefore malloc. */ |
||||
|
|
||||
|
#define BACKTRACE_USES_MALLOC @BACKTRACE_USES_MALLOC@ |
||||
|
|
||||
|
/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace
|
||||
|
library is configured with threading support, 0 if not. If this is |
||||
|
0, the threaded parameter to backtrace_create_state must be passed |
||||
|
as 0. */ |
||||
|
|
||||
|
#define BACKTRACE_SUPPORTS_THREADS @BACKTRACE_SUPPORTS_THREADS@ |
||||
|
|
||||
|
/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo
|
||||
|
will work for variables. It will always work for functions. */ |
||||
|
|
||||
|
#define BACKTRACE_SUPPORTS_DATA @BACKTRACE_SUPPORTS_DATA@ |
@ -0,0 +1,129 @@ |
|||||
|
/* backtrace.c -- Entry point for stack backtrace library.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "unwind.h" |
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* The main backtrace_full routine. */ |
||||
|
|
||||
|
/* Data passed through _Unwind_Backtrace. */ |
||||
|
|
||||
|
struct backtrace_data |
||||
|
{ |
||||
|
/* Number of frames to skip. */ |
||||
|
int skip; |
||||
|
/* Library state. */ |
||||
|
struct backtrace_state *state; |
||||
|
/* Callback routine. */ |
||||
|
backtrace_full_callback callback; |
||||
|
/* Error callback routine. */ |
||||
|
backtrace_error_callback error_callback; |
||||
|
/* Data to pass to callback routines. */ |
||||
|
void *data; |
||||
|
/* Value to return from backtrace_full. */ |
||||
|
int ret; |
||||
|
/* Whether there is any memory available. */ |
||||
|
int can_alloc; |
||||
|
}; |
||||
|
|
||||
|
/* Unwind library callback routine. This is passed to
|
||||
|
_Unwind_Backtrace. */ |
||||
|
|
||||
|
static _Unwind_Reason_Code |
||||
|
unwind (struct _Unwind_Context *context, void *vdata) |
||||
|
{ |
||||
|
struct backtrace_data *bdata = (struct backtrace_data *) vdata; |
||||
|
uintptr_t pc; |
||||
|
int ip_before_insn = 0; |
||||
|
|
||||
|
#ifdef HAVE_GETIPINFO |
||||
|
pc = _Unwind_GetIPInfo (context, &ip_before_insn); |
||||
|
#else |
||||
|
pc = _Unwind_GetIP (context); |
||||
|
#endif |
||||
|
|
||||
|
if (bdata->skip > 0) |
||||
|
{ |
||||
|
--bdata->skip; |
||||
|
return _URC_NO_REASON; |
||||
|
} |
||||
|
|
||||
|
if (!ip_before_insn) |
||||
|
--pc; |
||||
|
|
||||
|
if (!bdata->can_alloc) |
||||
|
bdata->ret = bdata->callback (bdata->data, pc, NULL, 0, NULL); |
||||
|
else |
||||
|
bdata->ret = backtrace_pcinfo (bdata->state, pc, bdata->callback, |
||||
|
bdata->error_callback, bdata->data); |
||||
|
if (bdata->ret != 0) |
||||
|
return _URC_END_OF_STACK; |
||||
|
|
||||
|
return _URC_NO_REASON; |
||||
|
} |
||||
|
|
||||
|
/* Get a stack backtrace. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_full (struct backtrace_state *state, int skip, |
||||
|
backtrace_full_callback callback, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
struct backtrace_data bdata; |
||||
|
void *p; |
||||
|
|
||||
|
bdata.skip = skip + 1; |
||||
|
bdata.state = state; |
||||
|
bdata.callback = callback; |
||||
|
bdata.error_callback = error_callback; |
||||
|
bdata.data = data; |
||||
|
bdata.ret = 0; |
||||
|
|
||||
|
/* If we can't allocate any memory at all, don't try to produce
|
||||
|
file/line information. */ |
||||
|
p = backtrace_alloc (state, 4096, NULL, NULL); |
||||
|
if (p == NULL) |
||||
|
bdata.can_alloc = 0; |
||||
|
else |
||||
|
{ |
||||
|
backtrace_free (state, p, 4096, NULL, NULL); |
||||
|
bdata.can_alloc = 1; |
||||
|
} |
||||
|
|
||||
|
_Unwind_Backtrace (unwind, &bdata); |
||||
|
return bdata.ret; |
||||
|
} |
@ -0,0 +1,182 @@ |
|||||
|
/* backtrace.h -- Public header file for stack backtrace library.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#ifndef BACKTRACE_H |
||||
|
#define BACKTRACE_H |
||||
|
|
||||
|
#include <stddef.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdio.h> |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
extern "C" { |
||||
|
#endif |
||||
|
|
||||
|
/* The backtrace state. This struct is intentionally not defined in
|
||||
|
the public interface. */ |
||||
|
|
||||
|
struct backtrace_state; |
||||
|
|
||||
|
/* The type of the error callback argument to backtrace functions.
|
||||
|
This function, if not NULL, will be called for certain error cases. |
||||
|
The DATA argument is passed to the function that calls this one. |
||||
|
The MSG argument is an error message. The ERRNUM argument, if |
||||
|
greater than 0, holds an errno value. The MSG buffer may become |
||||
|
invalid after this function returns. |
||||
|
|
||||
|
As a special case, the ERRNUM argument will be passed as -1 if no |
||||
|
debug info can be found for the executable, but the function |
||||
|
requires debug info (e.g., backtrace_full, backtrace_pcinfo). The |
||||
|
MSG in this case will be something along the lines of "no debug |
||||
|
info". Similarly, ERRNUM will be passed as -1 if there is no |
||||
|
symbol table, but the function requires a symbol table (e.g., |
||||
|
backtrace_syminfo). This may be used as a signal that some other |
||||
|
approach should be tried. */ |
||||
|
|
||||
|
typedef void (*backtrace_error_callback) (void *data, const char *msg, |
||||
|
int errnum); |
||||
|
|
||||
|
/* Create state information for the backtrace routines. This must be
|
||||
|
called before any of the other routines, and its return value must |
||||
|
be passed to all of the other routines. FILENAME is the path name |
||||
|
of the executable file; if it is NULL the library will try |
||||
|
system-specific path names. If not NULL, FILENAME must point to a |
||||
|
permanent buffer. If THREADED is non-zero the state may be |
||||
|
accessed by multiple threads simultaneously, and the library will |
||||
|
use appropriate atomic operations. If THREADED is zero the state |
||||
|
may only be accessed by one thread at a time. This returns a state |
||||
|
pointer on success, NULL on error. If an error occurs, this will |
||||
|
call the ERROR_CALLBACK routine. */ |
||||
|
|
||||
|
extern struct backtrace_state *backtrace_create_state ( |
||||
|
const char *filename, int threaded, |
||||
|
backtrace_error_callback error_callback, void *data); |
||||
|
|
||||
|
/* The type of the callback argument to the backtrace_full function.
|
||||
|
DATA is the argument passed to backtrace_full. PC is the program |
||||
|
counter. FILENAME is the name of the file containing PC, or NULL |
||||
|
if not available. LINENO is the line number in FILENAME containing |
||||
|
PC, or 0 if not available. FUNCTION is the name of the function |
||||
|
containing PC, or NULL if not available. This should return 0 to |
||||
|
continuing tracing. The FILENAME and FUNCTION buffers may become |
||||
|
invalid after this function returns. */ |
||||
|
|
||||
|
typedef int (*backtrace_full_callback) (void *data, uintptr_t pc, |
||||
|
const char *filename, int lineno, |
||||
|
const char *function); |
||||
|
|
||||
|
/* Get a full stack backtrace. SKIP is the number of frames to skip;
|
||||
|
passing 0 will start the trace with the function calling |
||||
|
backtrace_full. DATA is passed to the callback routine. If any |
||||
|
call to CALLBACK returns a non-zero value, the stack backtrace |
||||
|
stops, and backtrace returns that value; this may be used to limit |
||||
|
the number of stack frames desired. If all calls to CALLBACK |
||||
|
return 0, backtrace returns 0. The backtrace_full function will |
||||
|
make at least one call to either CALLBACK or ERROR_CALLBACK. This |
||||
|
function requires debug info for the executable. */ |
||||
|
|
||||
|
extern int backtrace_full (struct backtrace_state *state, int skip, |
||||
|
backtrace_full_callback callback, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* The type of the callback argument to the backtrace_simple function.
|
||||
|
DATA is the argument passed to simple_backtrace. PC is the program |
||||
|
counter. This should return 0 to continue tracing. */ |
||||
|
|
||||
|
typedef int (*backtrace_simple_callback) (void *data, uintptr_t pc); |
||||
|
|
||||
|
/* Get a simple backtrace. SKIP is the number of frames to skip, as
|
||||
|
in backtrace. DATA is passed to the callback routine. If any call |
||||
|
to CALLBACK returns a non-zero value, the stack backtrace stops, |
||||
|
and backtrace_simple returns that value. Otherwise |
||||
|
backtrace_simple returns 0. The backtrace_simple function will |
||||
|
make at least one call to either CALLBACK or ERROR_CALLBACK. This |
||||
|
function does not require any debug info for the executable. */ |
||||
|
|
||||
|
extern int backtrace_simple (struct backtrace_state *state, int skip, |
||||
|
backtrace_simple_callback callback, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* Print the current backtrace in a user readable format to a FILE.
|
||||
|
SKIP is the number of frames to skip, as in backtrace_full. Any |
||||
|
error messages are printed to stderr. This function requires debug |
||||
|
info for the executable. */ |
||||
|
|
||||
|
extern void backtrace_print (struct backtrace_state *state, int skip, FILE *); |
||||
|
|
||||
|
/* Given PC, a program counter in the current program, call the
|
||||
|
callback function with filename, line number, and function name |
||||
|
information. This will normally call the callback function exactly |
||||
|
once. However, if the PC happens to describe an inlined call, and |
||||
|
the debugging information contains the necessary information, then |
||||
|
this may call the callback function multiple times. This will make |
||||
|
at least one call to either CALLBACK or ERROR_CALLBACK. This |
||||
|
returns the first non-zero value returned by CALLBACK, or 0. */ |
||||
|
|
||||
|
extern int backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, |
||||
|
backtrace_full_callback callback, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* The type of the callback argument to backtrace_syminfo. DATA and
|
||||
|
PC are the arguments passed to backtrace_syminfo. SYMNAME is the |
||||
|
name of the symbol for the corresponding code. SYMVAL is the |
||||
|
value and SYMSIZE is the size of the symbol. SYMNAME will be NULL |
||||
|
if no error occurred but the symbol could not be found. */ |
||||
|
|
||||
|
typedef void (*backtrace_syminfo_callback) (void *data, uintptr_t pc, |
||||
|
const char *symname, |
||||
|
uintptr_t symval, |
||||
|
uintptr_t symsize); |
||||
|
|
||||
|
/* Given ADDR, an address or program counter in the current program,
|
||||
|
call the callback information with the symbol name and value |
||||
|
describing the function or variable in which ADDR may be found. |
||||
|
This will call either CALLBACK or ERROR_CALLBACK exactly once. |
||||
|
This returns 1 on success, 0 on failure. This function requires |
||||
|
the symbol table but does not require the debug info. Note that if |
||||
|
the symbol table is present but ADDR could not be found in the |
||||
|
table, CALLBACK will be called with a NULL SYMNAME argument. |
||||
|
Returns 1 on success, 0 on error. */ |
||||
|
|
||||
|
extern int backtrace_syminfo (struct backtrace_state *state, uintptr_t addr, |
||||
|
backtrace_syminfo_callback callback, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
#ifdef __cplusplus |
||||
|
} /* End extern "C". */ |
||||
|
#endif |
||||
|
|
||||
|
#endif |
@ -0,0 +1,500 @@ |
|||||
|
/* btest.c -- Test for libbacktrace library
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
/* This program tests the externally visible interfaces of the
|
||||
|
libbacktrace library. */ |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
#include "filenames.h" |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "backtrace-supported.h" |
||||
|
|
||||
|
#include "testlib.h" |
||||
|
|
||||
|
/* Test the backtrace function with non-inlined functions. */ |
||||
|
|
||||
|
static int test1 (void) __attribute__ ((noinline, unused)); |
||||
|
static int f2 (int) __attribute__ ((noinline)); |
||||
|
static int f3 (int, int) __attribute__ ((noinline)); |
||||
|
|
||||
|
static int |
||||
|
test1 (void) |
||||
|
{ |
||||
|
/* Returning a value here and elsewhere avoids a tailcall which
|
||||
|
would mess up the backtrace. */ |
||||
|
return f2 (__LINE__) + 1; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
f2 (int f1line) |
||||
|
{ |
||||
|
return f3 (f1line, __LINE__) + 2; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
f3 (int f1line, int f2line) |
||||
|
{ |
||||
|
struct info all[20]; |
||||
|
struct bdata data; |
||||
|
int f3line; |
||||
|
int i; |
||||
|
|
||||
|
data.all = &all[0]; |
||||
|
data.index = 0; |
||||
|
data.max = 20; |
||||
|
data.failed = 0; |
||||
|
|
||||
|
f3line = __LINE__ + 1; |
||||
|
i = backtrace_full (state, 0, callback_one, error_callback_one, &data); |
||||
|
|
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "test1: unexpected return value %d\n", i); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (data.index < 3) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test1: not enough frames; got %zu, expected at least 3\n", |
||||
|
data.index); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
check ("test1", 0, all, f3line, "f3", "btest.c", &data.failed); |
||||
|
check ("test1", 1, all, f2line, "f2", "btest.c", &data.failed); |
||||
|
check ("test1", 2, all, f1line, "test1", "btest.c", &data.failed); |
||||
|
|
||||
|
printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS"); |
||||
|
|
||||
|
if (data.failed) |
||||
|
++failures; |
||||
|
|
||||
|
return failures; |
||||
|
} |
||||
|
|
||||
|
/* Test the backtrace function with inlined functions. */ |
||||
|
|
||||
|
static inline int test2 (void) __attribute__ ((always_inline, unused)); |
||||
|
static inline int f12 (int) __attribute__ ((always_inline)); |
||||
|
static inline int f13 (int, int) __attribute__ ((always_inline)); |
||||
|
|
||||
|
static inline int |
||||
|
test2 (void) |
||||
|
{ |
||||
|
return f12 (__LINE__) + 1; |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
f12 (int f1line) |
||||
|
{ |
||||
|
return f13 (f1line, __LINE__) + 2; |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
f13 (int f1line, int f2line) |
||||
|
{ |
||||
|
struct info all[20]; |
||||
|
struct bdata data; |
||||
|
int f3line; |
||||
|
int i; |
||||
|
|
||||
|
data.all = &all[0]; |
||||
|
data.index = 0; |
||||
|
data.max = 20; |
||||
|
data.failed = 0; |
||||
|
|
||||
|
f3line = __LINE__ + 1; |
||||
|
i = backtrace_full (state, 0, callback_one, error_callback_one, &data); |
||||
|
|
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "test2: unexpected return value %d\n", i); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
check ("test2", 0, all, f3line, "f13", "btest.c", &data.failed); |
||||
|
check ("test2", 1, all, f2line, "f12", "btest.c", &data.failed); |
||||
|
check ("test2", 2, all, f1line, "test2", "btest.c", &data.failed); |
||||
|
|
||||
|
printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS"); |
||||
|
|
||||
|
if (data.failed) |
||||
|
++failures; |
||||
|
|
||||
|
return failures; |
||||
|
} |
||||
|
|
||||
|
/* Test the backtrace_simple function with non-inlined functions. */ |
||||
|
|
||||
|
static int test3 (void) __attribute__ ((noinline, unused)); |
||||
|
static int f22 (int) __attribute__ ((noinline)); |
||||
|
static int f23 (int, int) __attribute__ ((noinline)); |
||||
|
|
||||
|
static int |
||||
|
test3 (void) |
||||
|
{ |
||||
|
return f22 (__LINE__) + 1; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
f22 (int f1line) |
||||
|
{ |
||||
|
return f23 (f1line, __LINE__) + 2; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
f23 (int f1line, int f2line) |
||||
|
{ |
||||
|
uintptr_t addrs[20]; |
||||
|
struct sdata data; |
||||
|
int f3line; |
||||
|
int i; |
||||
|
|
||||
|
data.addrs = &addrs[0]; |
||||
|
data.index = 0; |
||||
|
data.max = 20; |
||||
|
data.failed = 0; |
||||
|
|
||||
|
f3line = __LINE__ + 1; |
||||
|
i = backtrace_simple (state, 0, callback_two, error_callback_two, &data); |
||||
|
|
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "test3: unexpected return value %d\n", i); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (!data.failed) |
||||
|
{ |
||||
|
struct info all[20]; |
||||
|
struct bdata bdata; |
||||
|
int j; |
||||
|
|
||||
|
bdata.all = &all[0]; |
||||
|
bdata.index = 0; |
||||
|
bdata.max = 20; |
||||
|
bdata.failed = 0; |
||||
|
|
||||
|
for (j = 0; j < 3; ++j) |
||||
|
{ |
||||
|
i = backtrace_pcinfo (state, addrs[j], callback_one, |
||||
|
error_callback_one, &bdata); |
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
("test3: unexpected return value " |
||||
|
"from backtrace_pcinfo %d\n"), |
||||
|
i); |
||||
|
bdata.failed = 1; |
||||
|
} |
||||
|
if (!bdata.failed && bdata.index != (size_t) (j + 1)) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
("wrong number of calls from backtrace_pcinfo " |
||||
|
"got %u expected %d\n"), |
||||
|
(unsigned int) bdata.index, j + 1); |
||||
|
bdata.failed = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
check ("test3", 0, all, f3line, "f23", "btest.c", &bdata.failed); |
||||
|
check ("test3", 1, all, f2line, "f22", "btest.c", &bdata.failed); |
||||
|
check ("test3", 2, all, f1line, "test3", "btest.c", &bdata.failed); |
||||
|
|
||||
|
if (bdata.failed) |
||||
|
data.failed = 1; |
||||
|
|
||||
|
for (j = 0; j < 3; ++j) |
||||
|
{ |
||||
|
struct symdata symdata; |
||||
|
|
||||
|
symdata.name = NULL; |
||||
|
symdata.val = 0; |
||||
|
symdata.size = 0; |
||||
|
symdata.failed = 0; |
||||
|
|
||||
|
i = backtrace_syminfo (state, addrs[j], callback_three, |
||||
|
error_callback_three, &symdata); |
||||
|
if (i == 0) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
("test3: [%d]: unexpected return value " |
||||
|
"from backtrace_syminfo %d\n"), |
||||
|
j, i); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (!symdata.failed) |
||||
|
{ |
||||
|
const char *expected; |
||||
|
|
||||
|
switch (j) |
||||
|
{ |
||||
|
case 0: |
||||
|
expected = "f23"; |
||||
|
break; |
||||
|
case 1: |
||||
|
expected = "f22"; |
||||
|
break; |
||||
|
case 2: |
||||
|
expected = "test3"; |
||||
|
break; |
||||
|
default: |
||||
|
assert (0); |
||||
|
} |
||||
|
|
||||
|
if (symdata.name == NULL) |
||||
|
{ |
||||
|
fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
/* Use strncmp, not strcmp, because GCC might create a
|
||||
|
clone. */ |
||||
|
else if (strncmp (symdata.name, expected, strlen (expected)) |
||||
|
!= 0) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
("test3: [%d]: unexpected syminfo name " |
||||
|
"got %s expected %s\n"), |
||||
|
j, symdata.name, expected); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (symdata.failed) |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS"); |
||||
|
|
||||
|
if (data.failed) |
||||
|
++failures; |
||||
|
|
||||
|
return failures; |
||||
|
} |
||||
|
|
||||
|
/* Test the backtrace_simple function with inlined functions. */ |
||||
|
|
||||
|
static inline int test4 (void) __attribute__ ((always_inline, unused)); |
||||
|
static inline int f32 (int) __attribute__ ((always_inline)); |
||||
|
static inline int f33 (int, int) __attribute__ ((always_inline)); |
||||
|
|
||||
|
static inline int |
||||
|
test4 (void) |
||||
|
{ |
||||
|
return f32 (__LINE__) + 1; |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
f32 (int f1line) |
||||
|
{ |
||||
|
return f33 (f1line, __LINE__) + 2; |
||||
|
} |
||||
|
|
||||
|
static inline int |
||||
|
f33 (int f1line, int f2line) |
||||
|
{ |
||||
|
uintptr_t addrs[20]; |
||||
|
struct sdata data; |
||||
|
int f3line; |
||||
|
int i; |
||||
|
|
||||
|
data.addrs = &addrs[0]; |
||||
|
data.index = 0; |
||||
|
data.max = 20; |
||||
|
data.failed = 0; |
||||
|
|
||||
|
f3line = __LINE__ + 1; |
||||
|
i = backtrace_simple (state, 0, callback_two, error_callback_two, &data); |
||||
|
|
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "test3: unexpected return value %d\n", i); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (!data.failed) |
||||
|
{ |
||||
|
struct info all[20]; |
||||
|
struct bdata bdata; |
||||
|
|
||||
|
bdata.all = &all[0]; |
||||
|
bdata.index = 0; |
||||
|
bdata.max = 20; |
||||
|
bdata.failed = 0; |
||||
|
|
||||
|
i = backtrace_pcinfo (state, addrs[0], callback_one, error_callback_one, |
||||
|
&bdata); |
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
("test4: unexpected return value " |
||||
|
"from backtrace_pcinfo %d\n"), |
||||
|
i); |
||||
|
bdata.failed = 1; |
||||
|
} |
||||
|
|
||||
|
check ("test4", 0, all, f3line, "f33", "btest.c", &bdata.failed); |
||||
|
check ("test4", 1, all, f2line, "f32", "btest.c", &bdata.failed); |
||||
|
check ("test4", 2, all, f1line, "test4", "btest.c", &bdata.failed); |
||||
|
|
||||
|
if (bdata.failed) |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
printf ("%s: backtrace_simple inline\n", data.failed ? "FAIL" : "PASS"); |
||||
|
|
||||
|
if (data.failed) |
||||
|
++failures; |
||||
|
|
||||
|
return failures; |
||||
|
} |
||||
|
|
||||
|
static int test5 (void) __attribute__ ((unused)); |
||||
|
|
||||
|
int global = 1; |
||||
|
|
||||
|
static int |
||||
|
test5 (void) |
||||
|
{ |
||||
|
struct symdata symdata; |
||||
|
int i; |
||||
|
uintptr_t addr = (uintptr_t) &global; |
||||
|
|
||||
|
if (sizeof (global) > 1) |
||||
|
addr += 1; |
||||
|
|
||||
|
symdata.name = NULL; |
||||
|
symdata.val = 0; |
||||
|
symdata.size = 0; |
||||
|
symdata.failed = 0; |
||||
|
|
||||
|
i = backtrace_syminfo (state, addr, callback_three, |
||||
|
error_callback_three, &symdata); |
||||
|
if (i == 0) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test5: unexpected return value from backtrace_syminfo %d\n", |
||||
|
i); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (!symdata.failed) |
||||
|
{ |
||||
|
if (symdata.name == NULL) |
||||
|
{ |
||||
|
fprintf (stderr, "test5: NULL syminfo name\n"); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
else if (strcmp (symdata.name, "global") != 0) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test5: unexpected syminfo name got %s expected %s\n", |
||||
|
symdata.name, "global"); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
else if (symdata.val != (uintptr_t) &global) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test5: unexpected syminfo value got %lx expected %lx\n", |
||||
|
(unsigned long) symdata.val, |
||||
|
(unsigned long) (uintptr_t) &global); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
else if (symdata.size != sizeof (global)) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test5: unexpected syminfo size got %lx expected %lx\n", |
||||
|
(unsigned long) symdata.size, |
||||
|
(unsigned long) sizeof (global)); |
||||
|
symdata.failed = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
printf ("%s: backtrace_syminfo variable\n", |
||||
|
symdata.failed ? "FAIL" : "PASS"); |
||||
|
|
||||
|
if (symdata.failed) |
||||
|
++failures; |
||||
|
|
||||
|
return failures; |
||||
|
} |
||||
|
|
||||
|
/* Check that are no files left open. */ |
||||
|
|
||||
|
static void |
||||
|
check_open_files (void) |
||||
|
{ |
||||
|
int i; |
||||
|
|
||||
|
for (i = 3; i < 10; i++) |
||||
|
{ |
||||
|
if (close (i) == 0) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"ERROR: descriptor %d still open after tests complete\n", |
||||
|
i); |
||||
|
++failures; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Run all the tests. */ |
||||
|
|
||||
|
int |
||||
|
main (int argc ATTRIBUTE_UNUSED, char **argv) |
||||
|
{ |
||||
|
state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, |
||||
|
error_callback_create, NULL); |
||||
|
|
||||
|
#if BACKTRACE_SUPPORTED |
||||
|
test1 (); |
||||
|
test2 (); |
||||
|
test3 (); |
||||
|
test4 (); |
||||
|
#if BACKTRACE_SUPPORTS_DATA |
||||
|
test5 (); |
||||
|
#endif |
||||
|
#endif |
||||
|
|
||||
|
check_open_files (); |
||||
|
|
||||
|
exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); |
||||
|
} |
File diff suppressed because it is too large
@ -0,0 +1,149 @@ |
|||||
|
/* config.h.in. Generated from configure.ac by autoheader. */ |
||||
|
|
||||
|
/* ELF size: 32 or 64 */ |
||||
|
#undef BACKTRACE_ELF_SIZE |
||||
|
|
||||
|
/* XCOFF size: 32 or 64 */ |
||||
|
#undef BACKTRACE_XCOFF_SIZE |
||||
|
|
||||
|
/* Define to 1 if you have the __atomic functions */ |
||||
|
#undef HAVE_ATOMIC_FUNCTIONS |
||||
|
|
||||
|
/* Define to 1 if you have the `clock_gettime' function. */ |
||||
|
#undef HAVE_CLOCK_GETTIME |
||||
|
|
||||
|
/* Define to 1 if you have the declaration of `strnlen', and to 0 if you
|
||||
|
don't. */ |
||||
|
#undef HAVE_DECL_STRNLEN |
||||
|
|
||||
|
/* Define to 1 if you have the <dlfcn.h> header file. */ |
||||
|
#undef HAVE_DLFCN_H |
||||
|
|
||||
|
/* Define if dl_iterate_phdr is available. */ |
||||
|
#undef HAVE_DL_ITERATE_PHDR |
||||
|
|
||||
|
/* Define to 1 if you have the fcntl function */ |
||||
|
#undef HAVE_FCNTL |
||||
|
|
||||
|
/* Define if getexecname is available. */ |
||||
|
#undef HAVE_GETEXECNAME |
||||
|
|
||||
|
/* Define if _Unwind_GetIPInfo is available. */ |
||||
|
#undef HAVE_GETIPINFO |
||||
|
|
||||
|
/* Define to 1 if you have the <inttypes.h> header file. */ |
||||
|
#undef HAVE_INTTYPES_H |
||||
|
|
||||
|
/* Define to 1 if you have the `z' library (-lz). */ |
||||
|
#undef HAVE_LIBZ |
||||
|
|
||||
|
/* Define to 1 if you have the <link.h> header file. */ |
||||
|
#undef HAVE_LINK_H |
||||
|
|
||||
|
/* Define if AIX loadquery is available. */ |
||||
|
#undef HAVE_LOADQUERY |
||||
|
|
||||
|
/* Define to 1 if you have the `lstat' function. */ |
||||
|
#undef HAVE_LSTAT |
||||
|
|
||||
|
/* Define to 1 if you have the <memory.h> header file. */ |
||||
|
#undef HAVE_MEMORY_H |
||||
|
|
||||
|
/* Define to 1 if you have the `readlink' function. */ |
||||
|
#undef HAVE_READLINK |
||||
|
|
||||
|
/* Define to 1 if you have the <stdint.h> header file. */ |
||||
|
#undef HAVE_STDINT_H |
||||
|
|
||||
|
/* Define to 1 if you have the <stdlib.h> header file. */ |
||||
|
#undef HAVE_STDLIB_H |
||||
|
|
||||
|
/* Define to 1 if you have the <strings.h> header file. */ |
||||
|
#undef HAVE_STRINGS_H |
||||
|
|
||||
|
/* Define to 1 if you have the <string.h> header file. */ |
||||
|
#undef HAVE_STRING_H |
||||
|
|
||||
|
/* Define to 1 if you have the __sync functions */ |
||||
|
#undef HAVE_SYNC_FUNCTIONS |
||||
|
|
||||
|
/* Define to 1 if you have the <sys/ldr.h> header file. */ |
||||
|
#undef HAVE_SYS_LDR_H |
||||
|
|
||||
|
/* Define to 1 if you have the <sys/mman.h> header file. */ |
||||
|
#undef HAVE_SYS_MMAN_H |
||||
|
|
||||
|
/* Define to 1 if you have the <sys/stat.h> header file. */ |
||||
|
#undef HAVE_SYS_STAT_H |
||||
|
|
||||
|
/* Define to 1 if you have the <sys/types.h> header file. */ |
||||
|
#undef HAVE_SYS_TYPES_H |
||||
|
|
||||
|
/* Define to 1 if you have the <unistd.h> header file. */ |
||||
|
#undef HAVE_UNISTD_H |
||||
|
|
||||
|
/* Define if -lz is available. */ |
||||
|
#undef HAVE_ZLIB |
||||
|
|
||||
|
/* Define to the sub-directory in which libtool stores uninstalled libraries.
|
||||
|
*/ |
||||
|
#undef LT_OBJDIR |
||||
|
|
||||
|
/* Define to the address where bug reports for this package should be sent. */ |
||||
|
#undef PACKAGE_BUGREPORT |
||||
|
|
||||
|
/* Define to the full name of this package. */ |
||||
|
#undef PACKAGE_NAME |
||||
|
|
||||
|
/* Define to the full name and version of this package. */ |
||||
|
#undef PACKAGE_STRING |
||||
|
|
||||
|
/* Define to the one symbol short name of this package. */ |
||||
|
#undef PACKAGE_TARNAME |
||||
|
|
||||
|
/* Define to the home page for this package. */ |
||||
|
#undef PACKAGE_URL |
||||
|
|
||||
|
/* Define to the version of this package. */ |
||||
|
#undef PACKAGE_VERSION |
||||
|
|
||||
|
/* Define to 1 if you have the ANSI C header files. */ |
||||
|
#undef STDC_HEADERS |
||||
|
|
||||
|
/* Enable extensions on AIX 3, Interix. */ |
||||
|
#ifndef _ALL_SOURCE |
||||
|
# undef _ALL_SOURCE |
||||
|
#endif |
||||
|
/* Enable GNU extensions on systems that have them. */ |
||||
|
#ifndef _GNU_SOURCE |
||||
|
# undef _GNU_SOURCE |
||||
|
#endif |
||||
|
/* Enable threading extensions on Solaris. */ |
||||
|
#ifndef _POSIX_PTHREAD_SEMANTICS |
||||
|
# undef _POSIX_PTHREAD_SEMANTICS |
||||
|
#endif |
||||
|
/* Enable extensions on HP NonStop. */ |
||||
|
#ifndef _TANDEM_SOURCE |
||||
|
# undef _TANDEM_SOURCE |
||||
|
#endif |
||||
|
/* Enable general extensions on Solaris. */ |
||||
|
#ifndef __EXTENSIONS__ |
||||
|
# undef __EXTENSIONS__ |
||||
|
#endif |
||||
|
|
||||
|
|
||||
|
/* Number of bits in a file offset, on hosts where this is settable. */ |
||||
|
#undef _FILE_OFFSET_BITS |
||||
|
|
||||
|
/* Define for large files, on AIX-style hosts. */ |
||||
|
#undef _LARGE_FILES |
||||
|
|
||||
|
/* Define to 1 if on MINIX. */ |
||||
|
#undef _MINIX |
||||
|
|
||||
|
/* Define to 2 if the system does not provide POSIX.1 features except with
|
||||
|
this defined. */ |
||||
|
#undef _POSIX_1_SOURCE |
||||
|
|
||||
|
/* Define to 1 if you need to in order for `stat' and other things to work. */ |
||||
|
#undef _POSIX_SOURCE |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,368 @@ |
|||||
|
# Helper functions for option handling. -*- Autoconf -*- |
||||
|
# |
||||
|
# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. |
||||
|
# Written by Gary V. Vaughan, 2004 |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation gives |
||||
|
# unlimited permission to copy and/or distribute it, with or without |
||||
|
# modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 6 ltoptions.m4 |
||||
|
|
||||
|
# This is to help aclocal find these macros, as it can't see m4_define. |
||||
|
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) |
||||
|
|
||||
|
|
||||
|
# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) |
||||
|
# ------------------------------------------ |
||||
|
m4_define([_LT_MANGLE_OPTION], |
||||
|
[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) |
||||
|
|
||||
|
|
||||
|
# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) |
||||
|
# --------------------------------------- |
||||
|
# Set option OPTION-NAME for macro MACRO-NAME, and if there is a |
||||
|
# matching handler defined, dispatch to it. Other OPTION-NAMEs are |
||||
|
# saved as a flag. |
||||
|
m4_define([_LT_SET_OPTION], |
||||
|
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl |
||||
|
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), |
||||
|
_LT_MANGLE_DEFUN([$1], [$2]), |
||||
|
[m4_warning([Unknown $1 option `$2'])])[]dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) |
||||
|
# ------------------------------------------------------------ |
||||
|
# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. |
||||
|
m4_define([_LT_IF_OPTION], |
||||
|
[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) |
||||
|
|
||||
|
|
||||
|
# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) |
||||
|
# ------------------------------------------------------- |
||||
|
# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME |
||||
|
# are set. |
||||
|
m4_define([_LT_UNLESS_OPTIONS], |
||||
|
[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), |
||||
|
[m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), |
||||
|
[m4_define([$0_found])])])[]dnl |
||||
|
m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 |
||||
|
])[]dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) |
||||
|
# ---------------------------------------- |
||||
|
# OPTION-LIST is a space-separated list of Libtool options associated |
||||
|
# with MACRO-NAME. If any OPTION has a matching handler declared with |
||||
|
# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about |
||||
|
# the unknown option and exit. |
||||
|
m4_defun([_LT_SET_OPTIONS], |
||||
|
[# Set options |
||||
|
m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), |
||||
|
[_LT_SET_OPTION([$1], _LT_Option)]) |
||||
|
|
||||
|
m4_if([$1],[LT_INIT],[ |
||||
|
dnl |
||||
|
dnl Simply set some default values (i.e off) if boolean options were not |
||||
|
dnl specified: |
||||
|
_LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no |
||||
|
]) |
||||
|
_LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no |
||||
|
]) |
||||
|
dnl |
||||
|
dnl If no reference was made to various pairs of opposing options, then |
||||
|
dnl we run the default mode handler for the pair. For example, if neither |
||||
|
dnl `shared' nor `disable-shared' was passed, we enable building of shared |
||||
|
dnl archives by default: |
||||
|
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) |
||||
|
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) |
||||
|
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) |
||||
|
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], |
||||
|
[_LT_ENABLE_FAST_INSTALL]) |
||||
|
]) |
||||
|
])# _LT_SET_OPTIONS |
||||
|
|
||||
|
|
||||
|
## --------------------------------- ## |
||||
|
## Macros to handle LT_INIT options. ## |
||||
|
## --------------------------------- ## |
||||
|
|
||||
|
# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) |
||||
|
# ----------------------------------------- |
||||
|
m4_define([_LT_MANGLE_DEFUN], |
||||
|
[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) |
||||
|
|
||||
|
|
||||
|
# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) |
||||
|
# ----------------------------------------------- |
||||
|
m4_define([LT_OPTION_DEFINE], |
||||
|
[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl |
||||
|
])# LT_OPTION_DEFINE |
||||
|
|
||||
|
|
||||
|
# dlopen |
||||
|
# ------ |
||||
|
LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes |
||||
|
]) |
||||
|
|
||||
|
AU_DEFUN([AC_LIBTOOL_DLOPEN], |
||||
|
[_LT_SET_OPTION([LT_INIT], [dlopen]) |
||||
|
AC_DIAGNOSE([obsolete], |
||||
|
[$0: Remove this warning and the call to _LT_SET_OPTION when you |
||||
|
put the `dlopen' option into LT_INIT's first parameter.]) |
||||
|
]) |
||||
|
|
||||
|
dnl aclocal-1.4 backwards compatibility: |
||||
|
dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) |
||||
|
|
||||
|
|
||||
|
# win32-dll |
||||
|
# --------- |
||||
|
# Declare package support for building win32 dll's. |
||||
|
LT_OPTION_DEFINE([LT_INIT], [win32-dll], |
||||
|
[enable_win32_dll=yes |
||||
|
|
||||
|
case $host in |
||||
|
*-*-cygwin* | *-*-mingw* | *-*-pw32*) |
||||
|
AC_CHECK_TOOL(AS, as, false) |
||||
|
AC_CHECK_TOOL(DLLTOOL, dlltool, false) |
||||
|
AC_CHECK_TOOL(OBJDUMP, objdump, false) |
||||
|
;; |
||||
|
esac |
||||
|
|
||||
|
test -z "$AS" && AS=as |
||||
|
_LT_DECL([], [AS], [0], [Assembler program])dnl |
||||
|
|
||||
|
test -z "$DLLTOOL" && DLLTOOL=dlltool |
||||
|
_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl |
||||
|
|
||||
|
test -z "$OBJDUMP" && OBJDUMP=objdump |
||||
|
_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl |
||||
|
])# win32-dll |
||||
|
|
||||
|
AU_DEFUN([AC_LIBTOOL_WIN32_DLL], |
||||
|
[AC_REQUIRE([AC_CANONICAL_HOST])dnl |
||||
|
_LT_SET_OPTION([LT_INIT], [win32-dll]) |
||||
|
AC_DIAGNOSE([obsolete], |
||||
|
[$0: Remove this warning and the call to _LT_SET_OPTION when you |
||||
|
put the `win32-dll' option into LT_INIT's first parameter.]) |
||||
|
]) |
||||
|
|
||||
|
dnl aclocal-1.4 backwards compatibility: |
||||
|
dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) |
||||
|
|
||||
|
|
||||
|
# _LT_ENABLE_SHARED([DEFAULT]) |
||||
|
# ---------------------------- |
||||
|
# implement the --enable-shared flag, and supports the `shared' and |
||||
|
# `disable-shared' LT_INIT options. |
||||
|
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. |
||||
|
m4_define([_LT_ENABLE_SHARED], |
||||
|
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl |
||||
|
AC_ARG_ENABLE([shared], |
||||
|
[AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], |
||||
|
[build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], |
||||
|
[p=${PACKAGE-default} |
||||
|
case $enableval in |
||||
|
yes) enable_shared=yes ;; |
||||
|
no) enable_shared=no ;; |
||||
|
*) |
||||
|
enable_shared=no |
||||
|
# Look at the argument we got. We use all the common list separators. |
||||
|
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," |
||||
|
for pkg in $enableval; do |
||||
|
IFS="$lt_save_ifs" |
||||
|
if test "X$pkg" = "X$p"; then |
||||
|
enable_shared=yes |
||||
|
fi |
||||
|
done |
||||
|
IFS="$lt_save_ifs" |
||||
|
;; |
||||
|
esac], |
||||
|
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT) |
||||
|
|
||||
|
_LT_DECL([build_libtool_libs], [enable_shared], [0], |
||||
|
[Whether or not to build shared libraries]) |
||||
|
])# _LT_ENABLE_SHARED |
||||
|
|
||||
|
LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) |
||||
|
LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) |
||||
|
|
||||
|
# Old names: |
||||
|
AC_DEFUN([AC_ENABLE_SHARED], |
||||
|
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) |
||||
|
]) |
||||
|
|
||||
|
AC_DEFUN([AC_DISABLE_SHARED], |
||||
|
[_LT_SET_OPTION([LT_INIT], [disable-shared]) |
||||
|
]) |
||||
|
|
||||
|
AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) |
||||
|
AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) |
||||
|
|
||||
|
dnl aclocal-1.4 backwards compatibility: |
||||
|
dnl AC_DEFUN([AM_ENABLE_SHARED], []) |
||||
|
dnl AC_DEFUN([AM_DISABLE_SHARED], []) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# _LT_ENABLE_STATIC([DEFAULT]) |
||||
|
# ---------------------------- |
||||
|
# implement the --enable-static flag, and support the `static' and |
||||
|
# `disable-static' LT_INIT options. |
||||
|
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. |
||||
|
m4_define([_LT_ENABLE_STATIC], |
||||
|
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl |
||||
|
AC_ARG_ENABLE([static], |
||||
|
[AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], |
||||
|
[build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], |
||||
|
[p=${PACKAGE-default} |
||||
|
case $enableval in |
||||
|
yes) enable_static=yes ;; |
||||
|
no) enable_static=no ;; |
||||
|
*) |
||||
|
enable_static=no |
||||
|
# Look at the argument we got. We use all the common list separators. |
||||
|
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," |
||||
|
for pkg in $enableval; do |
||||
|
IFS="$lt_save_ifs" |
||||
|
if test "X$pkg" = "X$p"; then |
||||
|
enable_static=yes |
||||
|
fi |
||||
|
done |
||||
|
IFS="$lt_save_ifs" |
||||
|
;; |
||||
|
esac], |
||||
|
[enable_static=]_LT_ENABLE_STATIC_DEFAULT) |
||||
|
|
||||
|
_LT_DECL([build_old_libs], [enable_static], [0], |
||||
|
[Whether or not to build static libraries]) |
||||
|
])# _LT_ENABLE_STATIC |
||||
|
|
||||
|
LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) |
||||
|
LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) |
||||
|
|
||||
|
# Old names: |
||||
|
AC_DEFUN([AC_ENABLE_STATIC], |
||||
|
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) |
||||
|
]) |
||||
|
|
||||
|
AC_DEFUN([AC_DISABLE_STATIC], |
||||
|
[_LT_SET_OPTION([LT_INIT], [disable-static]) |
||||
|
]) |
||||
|
|
||||
|
AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) |
||||
|
AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) |
||||
|
|
||||
|
dnl aclocal-1.4 backwards compatibility: |
||||
|
dnl AC_DEFUN([AM_ENABLE_STATIC], []) |
||||
|
dnl AC_DEFUN([AM_DISABLE_STATIC], []) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# _LT_ENABLE_FAST_INSTALL([DEFAULT]) |
||||
|
# ---------------------------------- |
||||
|
# implement the --enable-fast-install flag, and support the `fast-install' |
||||
|
# and `disable-fast-install' LT_INIT options. |
||||
|
# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. |
||||
|
m4_define([_LT_ENABLE_FAST_INSTALL], |
||||
|
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl |
||||
|
AC_ARG_ENABLE([fast-install], |
||||
|
[AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], |
||||
|
[optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], |
||||
|
[p=${PACKAGE-default} |
||||
|
case $enableval in |
||||
|
yes) enable_fast_install=yes ;; |
||||
|
no) enable_fast_install=no ;; |
||||
|
*) |
||||
|
enable_fast_install=no |
||||
|
# Look at the argument we got. We use all the common list separators. |
||||
|
lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," |
||||
|
for pkg in $enableval; do |
||||
|
IFS="$lt_save_ifs" |
||||
|
if test "X$pkg" = "X$p"; then |
||||
|
enable_fast_install=yes |
||||
|
fi |
||||
|
done |
||||
|
IFS="$lt_save_ifs" |
||||
|
;; |
||||
|
esac], |
||||
|
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) |
||||
|
|
||||
|
_LT_DECL([fast_install], [enable_fast_install], [0], |
||||
|
[Whether or not to optimize for fast installation])dnl |
||||
|
])# _LT_ENABLE_FAST_INSTALL |
||||
|
|
||||
|
LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) |
||||
|
LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) |
||||
|
|
||||
|
# Old names: |
||||
|
AU_DEFUN([AC_ENABLE_FAST_INSTALL], |
||||
|
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) |
||||
|
AC_DIAGNOSE([obsolete], |
||||
|
[$0: Remove this warning and the call to _LT_SET_OPTION when you put |
||||
|
the `fast-install' option into LT_INIT's first parameter.]) |
||||
|
]) |
||||
|
|
||||
|
AU_DEFUN([AC_DISABLE_FAST_INSTALL], |
||||
|
[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) |
||||
|
AC_DIAGNOSE([obsolete], |
||||
|
[$0: Remove this warning and the call to _LT_SET_OPTION when you put |
||||
|
the `disable-fast-install' option into LT_INIT's first parameter.]) |
||||
|
]) |
||||
|
|
||||
|
dnl aclocal-1.4 backwards compatibility: |
||||
|
dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) |
||||
|
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) |
||||
|
|
||||
|
|
||||
|
# _LT_WITH_PIC([MODE]) |
||||
|
# -------------------- |
||||
|
# implement the --with-pic flag, and support the `pic-only' and `no-pic' |
||||
|
# LT_INIT options. |
||||
|
# MODE is either `yes' or `no'. If omitted, it defaults to `both'. |
||||
|
m4_define([_LT_WITH_PIC], |
||||
|
[AC_ARG_WITH([pic], |
||||
|
[AS_HELP_STRING([--with-pic], |
||||
|
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])], |
||||
|
[pic_mode="$withval"], |
||||
|
[pic_mode=default]) |
||||
|
|
||||
|
test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) |
||||
|
|
||||
|
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl |
||||
|
])# _LT_WITH_PIC |
||||
|
|
||||
|
LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) |
||||
|
LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) |
||||
|
|
||||
|
# Old name: |
||||
|
AU_DEFUN([AC_LIBTOOL_PICMODE], |
||||
|
[_LT_SET_OPTION([LT_INIT], [pic-only]) |
||||
|
AC_DIAGNOSE([obsolete], |
||||
|
[$0: Remove this warning and the call to _LT_SET_OPTION when you |
||||
|
put the `pic-only' option into LT_INIT's first parameter.]) |
||||
|
]) |
||||
|
|
||||
|
dnl aclocal-1.4 backwards compatibility: |
||||
|
dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) |
||||
|
|
||||
|
## ----------------- ## |
||||
|
## LTDL_INIT Options ## |
||||
|
## ----------------- ## |
||||
|
|
||||
|
m4_define([_LTDL_MODE], []) |
||||
|
LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], |
||||
|
[m4_define([_LTDL_MODE], [nonrecursive])]) |
||||
|
LT_OPTION_DEFINE([LTDL_INIT], [recursive], |
||||
|
[m4_define([_LTDL_MODE], [recursive])]) |
||||
|
LT_OPTION_DEFINE([LTDL_INIT], [subproject], |
||||
|
[m4_define([_LTDL_MODE], [subproject])]) |
||||
|
|
||||
|
m4_define([_LTDL_TYPE], []) |
||||
|
LT_OPTION_DEFINE([LTDL_INIT], [installable], |
||||
|
[m4_define([_LTDL_TYPE], [installable])]) |
||||
|
LT_OPTION_DEFINE([LTDL_INIT], [convenience], |
||||
|
[m4_define([_LTDL_TYPE], [convenience])]) |
@ -0,0 +1,123 @@ |
|||||
|
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- |
||||
|
# |
||||
|
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. |
||||
|
# Written by Gary V. Vaughan, 2004 |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation gives |
||||
|
# unlimited permission to copy and/or distribute it, with or without |
||||
|
# modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 5 ltsugar.m4 |
||||
|
|
||||
|
# This is to help aclocal find these macros, as it can't see m4_define. |
||||
|
AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) |
||||
|
|
||||
|
|
||||
|
# lt_join(SEP, ARG1, [ARG2...]) |
||||
|
# ----------------------------- |
||||
|
# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their |
||||
|
# associated separator. |
||||
|
# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier |
||||
|
# versions in m4sugar had bugs. |
||||
|
m4_define([lt_join], |
||||
|
[m4_if([$#], [1], [], |
||||
|
[$#], [2], [[$2]], |
||||
|
[m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) |
||||
|
m4_define([_lt_join], |
||||
|
[m4_if([$#$2], [2], [], |
||||
|
[m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) |
||||
|
|
||||
|
|
||||
|
# lt_car(LIST) |
||||
|
# lt_cdr(LIST) |
||||
|
# ------------ |
||||
|
# Manipulate m4 lists. |
||||
|
# These macros are necessary as long as will still need to support |
||||
|
# Autoconf-2.59 which quotes differently. |
||||
|
m4_define([lt_car], [[$1]]) |
||||
|
m4_define([lt_cdr], |
||||
|
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], |
||||
|
[$#], 1, [], |
||||
|
[m4_dquote(m4_shift($@))])]) |
||||
|
m4_define([lt_unquote], $1) |
||||
|
|
||||
|
|
||||
|
# lt_append(MACRO-NAME, STRING, [SEPARATOR]) |
||||
|
# ------------------------------------------ |
||||
|
# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. |
||||
|
# Note that neither SEPARATOR nor STRING are expanded; they are appended |
||||
|
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). |
||||
|
# No SEPARATOR is output if MACRO-NAME was previously undefined (different |
||||
|
# than defined and empty). |
||||
|
# |
||||
|
# This macro is needed until we can rely on Autoconf 2.62, since earlier |
||||
|
# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. |
||||
|
m4_define([lt_append], |
||||
|
[m4_define([$1], |
||||
|
m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) |
||||
|
|
||||
|
|
||||
|
|
||||
|
# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) |
||||
|
# ---------------------------------------------------------- |
||||
|
# Produce a SEP delimited list of all paired combinations of elements of |
||||
|
# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list |
||||
|
# has the form PREFIXmINFIXSUFFIXn. |
||||
|
m4_define([lt_combine], |
||||
|
[m4_if([$2], [], [], |
||||
|
[m4_if([$4], [], [], |
||||
|
[lt_join(m4_quote(m4_default([$1], [[, ]])), |
||||
|
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_prefix, [$2], |
||||
|
[m4_foreach(_Lt_suffix, lt_car([m4_shiftn(3, $@)]), |
||||
|
[_Lt_prefix[]$3[]_Lt_suffix ])])))))])])dnl |
||||
|
]) |
||||
|
|
||||
|
|
||||
|
# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) |
||||
|
# ----------------------------------------------------------------------- |
||||
|
# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited |
||||
|
# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. |
||||
|
m4_define([lt_if_append_uniq], |
||||
|
[m4_ifdef([$1], |
||||
|
[m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], |
||||
|
[lt_append([$1], [$2], [$3])$4], |
||||
|
[$5])], |
||||
|
[lt_append([$1], [$2], [$3])$4])]) |
||||
|
|
||||
|
|
||||
|
# lt_dict_add(DICT, KEY, VALUE) |
||||
|
# ----------------------------- |
||||
|
m4_define([lt_dict_add], |
||||
|
[m4_define([$1($2)], [$3])]) |
||||
|
|
||||
|
|
||||
|
# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) |
||||
|
# -------------------------------------------- |
||||
|
m4_define([lt_dict_add_subkey], |
||||
|
[m4_define([$1($2:$3)], [$4])]) |
||||
|
|
||||
|
|
||||
|
# lt_dict_fetch(DICT, KEY, [SUBKEY]) |
||||
|
# ---------------------------------- |
||||
|
m4_define([lt_dict_fetch], |
||||
|
[m4_ifval([$3], |
||||
|
m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), |
||||
|
m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) |
||||
|
|
||||
|
|
||||
|
# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) |
||||
|
# ----------------------------------------------------------------- |
||||
|
m4_define([lt_if_dict_fetch], |
||||
|
[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], |
||||
|
[$5], |
||||
|
[$6])]) |
||||
|
|
||||
|
|
||||
|
# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) |
||||
|
# -------------------------------------------------------------- |
||||
|
m4_define([lt_dict_filter], |
||||
|
[m4_if([$5], [], [], |
||||
|
[lt_join(m4_quote(m4_default([$4], [[, ]])), |
||||
|
lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), |
||||
|
[lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl |
||||
|
]) |
@ -0,0 +1,23 @@ |
|||||
|
# ltversion.m4 -- version numbers -*- Autoconf -*- |
||||
|
# |
||||
|
# Copyright (C) 2004 Free Software Foundation, Inc. |
||||
|
# Written by Scott James Remnant, 2004 |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation gives |
||||
|
# unlimited permission to copy and/or distribute it, with or without |
||||
|
# modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# Generated from ltversion.in. |
||||
|
|
||||
|
# serial 2976 ltversion.m4 |
||||
|
# This file is part of GNU Libtool |
||||
|
|
||||
|
m4_define([LT_PACKAGE_VERSION], [2.2.4]) |
||||
|
m4_define([LT_PACKAGE_REVISION], [1.2976]) |
||||
|
|
||||
|
AC_DEFUN([LTVERSION_VERSION], |
||||
|
[macro_version='2.2.4' |
||||
|
macro_revision='1.2976' |
||||
|
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) |
||||
|
_LT_DECL(, macro_revision, 0) |
||||
|
]) |
@ -0,0 +1,92 @@ |
|||||
|
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- |
||||
|
# |
||||
|
# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. |
||||
|
# Written by Scott James Remnant, 2004. |
||||
|
# |
||||
|
# This file is free software; the Free Software Foundation gives |
||||
|
# unlimited permission to copy and/or distribute it, with or without |
||||
|
# modifications, as long as this notice is preserved. |
||||
|
|
||||
|
# serial 4 lt~obsolete.m4 |
||||
|
|
||||
|
# These exist entirely to fool aclocal when bootstrapping libtool. |
||||
|
# |
||||
|
# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) |
||||
|
# which have later been changed to m4_define as they aren't part of the |
||||
|
# exported API, or moved to Autoconf or Automake where they belong. |
||||
|
# |
||||
|
# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN |
||||
|
# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us |
||||
|
# using a macro with the same name in our local m4/libtool.m4 it'll |
||||
|
# pull the old libtool.m4 in (it doesn't see our shiny new m4_define |
||||
|
# and doesn't know about Autoconf macros at all.) |
||||
|
# |
||||
|
# So we provide this file, which has a silly filename so it's always |
||||
|
# included after everything else. This provides aclocal with the |
||||
|
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything |
||||
|
# because those macros already exist, or will be overwritten later. |
||||
|
# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. |
||||
|
# |
||||
|
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. |
||||
|
# Yes, that means every name once taken will need to remain here until |
||||
|
# we give up compatibility with versions before 1.7, at which point |
||||
|
# we need to keep only those names which we still refer to. |
||||
|
|
||||
|
# This is to help aclocal find these macros, as it can't see m4_define. |
||||
|
AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) |
||||
|
|
||||
|
m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) |
||||
|
m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) |
||||
|
m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) |
||||
|
m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) |
||||
|
m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) |
||||
|
m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) |
||||
|
m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) |
||||
|
m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) |
||||
|
m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) |
||||
|
m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) |
||||
|
m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) |
||||
|
m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) |
||||
|
m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) |
||||
|
m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) |
||||
|
m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) |
||||
|
m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) |
||||
|
m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) |
||||
|
m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) |
||||
|
m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) |
||||
|
m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) |
||||
|
m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) |
||||
|
m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) |
||||
|
m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) |
||||
|
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) |
||||
|
m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) |
||||
|
m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) |
||||
|
m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) |
||||
|
m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) |
||||
|
m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) |
||||
|
m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) |
||||
|
m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) |
||||
|
m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) |
||||
|
m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) |
||||
|
m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) |
||||
|
m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) |
||||
|
m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) |
||||
|
m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) |
||||
|
m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) |
||||
|
m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) |
||||
|
m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) |
||||
|
m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) |
||||
|
m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) |
||||
|
m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])]) |
||||
|
m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) |
||||
|
m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) |
||||
|
m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) |
||||
|
m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) |
||||
|
m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) |
||||
|
m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) |
||||
|
m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) |
||||
|
m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) |
||||
|
m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) |
||||
|
m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) |
||||
|
m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) |
||||
|
m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) |
File diff suppressed because it is too large
@ -0,0 +1,503 @@ |
|||||
|
# configure.ac -- Backtrace configure script. |
||||
|
# Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
|
||||
|
# Redistribution and use in source and binary forms, with or without |
||||
|
# modification, are permitted provided that the following conditions are |
||||
|
# met: |
||||
|
|
||||
|
# (1) Redistributions of source code must retain the above copyright |
||||
|
# notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
# (2) Redistributions in binary form must reproduce the above copyright |
||||
|
# notice, this list of conditions and the following disclaimer in |
||||
|
# the documentation and/or other materials provided with the |
||||
|
# distribution. |
||||
|
|
||||
|
# (3) The name of the author may not be used to |
||||
|
# endorse or promote products derived from this software without |
||||
|
# specific prior written permission. |
||||
|
|
||||
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
# POSSIBILITY OF SUCH DAMAGE. |
||||
|
|
||||
|
AC_PREREQ(2.64) |
||||
|
AC_INIT(package-unused, version-unused,, libbacktrace) |
||||
|
AC_CONFIG_SRCDIR(backtrace.h) |
||||
|
AC_CONFIG_HEADER(config.h) |
||||
|
AC_CONFIG_MACRO_DIR(config) |
||||
|
|
||||
|
# with_target_subdir is used when configured as part of a GCC tree. |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
AM_ENABLE_MULTILIB(, ..) |
||||
|
fi |
||||
|
|
||||
|
AC_CANONICAL_SYSTEM |
||||
|
target_alias=${target_alias-$host_alias} |
||||
|
|
||||
|
AC_USE_SYSTEM_EXTENSIONS |
||||
|
|
||||
|
libtool_VERSION=1:0:0 |
||||
|
AC_SUBST(libtool_VERSION) |
||||
|
|
||||
|
# 1.11.1: Require that version of automake. |
||||
|
# foreign: Don't require README, INSTALL, NEWS, etc. |
||||
|
# no-define: Don't define PACKAGE and VERSION. |
||||
|
# no-dependencies: Don't generate automatic dependencies. |
||||
|
# (because it breaks when using bootstrap-lean, since some of the |
||||
|
# headers are gone at "make install" time). |
||||
|
# -Wall: Issue all automake warnings. |
||||
|
# -Wno-portability: Don't warn about constructs supported by GNU make. |
||||
|
# (because GCC requires GNU make anyhow). |
||||
|
AM_INIT_AUTOMAKE([1.11.1 foreign no-dist no-define no-dependencies -Wall -Wno-portability]) |
||||
|
|
||||
|
AM_MAINTAINER_MODE |
||||
|
|
||||
|
AC_ARG_WITH(target-subdir, |
||||
|
[ --with-target-subdir=SUBDIR Configuring in a subdirectory for target]) |
||||
|
|
||||
|
# We must force CC to /not/ be precious variables; otherwise |
||||
|
# the wrong, non-multilib-adjusted value will be used in multilibs. |
||||
|
# As a side effect, we have to subst CFLAGS ourselves. |
||||
|
m4_rename([_AC_ARG_VAR_PRECIOUS],[backtrace_PRECIOUS]) |
||||
|
m4_define([_AC_ARG_VAR_PRECIOUS],[]) |
||||
|
AC_PROG_CC |
||||
|
m4_rename_force([backtrace_PRECIOUS],[_AC_ARG_VAR_PRECIOUS]) |
||||
|
|
||||
|
AC_SUBST(CFLAGS) |
||||
|
|
||||
|
AC_PROG_AWK |
||||
|
case "$AWK" in |
||||
|
"") AC_MSG_ERROR([can't build without awk]) ;; |
||||
|
esac |
||||
|
|
||||
|
LT_INIT |
||||
|
AM_PROG_LIBTOOL |
||||
|
|
||||
|
AC_SYS_LARGEFILE |
||||
|
|
||||
|
backtrace_supported=yes |
||||
|
|
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
# We are compiling a GCC library. We can assume that the unwind |
||||
|
# library exists. |
||||
|
BACKTRACE_FILE="backtrace.lo simple.lo" |
||||
|
else |
||||
|
AC_CHECK_HEADER([unwind.h], |
||||
|
[AC_CHECK_FUNC([_Unwind_Backtrace], |
||||
|
[BACKTRACE_FILE="backtrace.lo simple.lo"], |
||||
|
[BACKTRACE_FILE="nounwind.lo" |
||||
|
backtrace_supported=no])], |
||||
|
[BACKTRACE_FILE="nounwind.lo" |
||||
|
backtrace_supported=no]) |
||||
|
fi |
||||
|
AC_SUBST(BACKTRACE_FILE) |
||||
|
|
||||
|
EXTRA_FLAGS= |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
EXTRA_FLAGS="-funwind-tables -frandom-seed=\$@" |
||||
|
else |
||||
|
AC_CACHE_CHECK([for -funwind-tables option], |
||||
|
[libbacktrace_cv_c_unwind_tables], |
||||
|
[CFLAGS_hold="$CFLAGS" |
||||
|
CFLAGS="$CFLAGS -funwind-tables" |
||||
|
AC_COMPILE_IFELSE( |
||||
|
[AC_LANG_PROGRAM([static int f() { return 0; }], [return f();])], |
||||
|
[libbacktrace_cv_c_unwind_tables=yes], |
||||
|
[libbacktrace_cv_c_unwind_tables=no]) |
||||
|
CFLAGS="$CFLAGS_hold"]) |
||||
|
if test "$libbacktrace_cv_c_unwind_tables" = "yes"; then |
||||
|
EXTRA_FLAGS=-funwind-tables |
||||
|
fi |
||||
|
AC_CACHE_CHECK([for -frandom-seed=string option], |
||||
|
[libbacktrace_cv_c_random_seed_string], |
||||
|
[CFLAGS_hold="$CFLAGS" |
||||
|
CFLAGS="$CFLAGS -frandom-seed=conftest.lo" |
||||
|
AC_COMPILE_IFELSE( |
||||
|
[AC_LANG_PROGRAM([], [return 0;])], |
||||
|
[libbacktrace_cv_c_random_seed_string=yes], |
||||
|
[libbacktrace_cv_c_random_seed_string=no]) |
||||
|
CFLAGS="$CFLAGS_hold"]) |
||||
|
if test "$libbacktrace_cv_c_random_seed_string" = "yes"; then |
||||
|
EXTRA_FLAGS="$EXTRA_FLAGS -frandom-seed=\$@" |
||||
|
fi |
||||
|
fi |
||||
|
AC_SUBST(EXTRA_FLAGS) |
||||
|
|
||||
|
ACX_PROG_CC_WARNING_OPTS([-W -Wall -Wwrite-strings -Wstrict-prototypes \ |
||||
|
-Wmissing-prototypes -Wold-style-definition \ |
||||
|
-Wmissing-format-attribute -Wcast-qual], |
||||
|
[WARN_FLAGS]) |
||||
|
|
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
WARN_FLAGS="$WARN_FLAGS -Werror" |
||||
|
fi |
||||
|
|
||||
|
AC_SUBST(WARN_FLAGS) |
||||
|
|
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
GCC_CHECK_UNWIND_GETIPINFO |
||||
|
else |
||||
|
ac_save_CFFLAGS="$CFLAGS" |
||||
|
CFLAGS="$CFLAGS -Werror-implicit-function-declaration" |
||||
|
AC_MSG_CHECKING([for _Unwind_GetIPInfo]) |
||||
|
AC_LINK_IFELSE( |
||||
|
[AC_LANG_PROGRAM( |
||||
|
[#include "unwind.h" |
||||
|
struct _Unwind_Context *context; |
||||
|
int ip_before_insn = 0;], |
||||
|
[return _Unwind_GetIPInfo (context, &ip_before_insn);])], |
||||
|
[have_unwind_getipinfo=yes], [have_unwind_getipinfo=no]) |
||||
|
CFLAGS="$ac_save_CFLAGS" |
||||
|
AC_MSG_RESULT([$have_unwind_getipinfo]) |
||||
|
if test "$have_unwind_getipinfo" = "yes"; then |
||||
|
AC_DEFINE(HAVE_GETIPINFO, 1, [Define if _Unwind_GetIPInfo is available.]) |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
# Enable --enable-host-shared. |
||||
|
AC_ARG_ENABLE(host-shared, |
||||
|
[AS_HELP_STRING([--enable-host-shared], |
||||
|
[build host code as shared libraries])], |
||||
|
[PIC_FLAG=-fPIC], [PIC_FLAG=]) |
||||
|
AC_SUBST(PIC_FLAG) |
||||
|
|
||||
|
# Test for __sync support. |
||||
|
AC_CACHE_CHECK([__sync extensions], |
||||
|
[libbacktrace_cv_sys_sync], |
||||
|
[if test -n "${with_target_subdir}"; then |
||||
|
case "${host}" in |
||||
|
hppa*-*-hpux*) libbacktrace_cv_sys_sync=no ;; |
||||
|
*) libbacktrace_cv_sys_sync=yes ;; |
||||
|
esac |
||||
|
else |
||||
|
AC_LINK_IFELSE( |
||||
|
[AC_LANG_PROGRAM([int i;], |
||||
|
[__sync_bool_compare_and_swap (&i, i, i); |
||||
|
__sync_lock_test_and_set (&i, 1); |
||||
|
__sync_lock_release (&i);])], |
||||
|
[libbacktrace_cv_sys_sync=yes], |
||||
|
[libbacktrace_cv_sys_sync=no]) |
||||
|
fi]) |
||||
|
BACKTRACE_SUPPORTS_THREADS=0 |
||||
|
if test "$libbacktrace_cv_sys_sync" = "yes"; then |
||||
|
BACKTRACE_SUPPORTS_THREADS=1 |
||||
|
AC_DEFINE([HAVE_SYNC_FUNCTIONS], 1, |
||||
|
[Define to 1 if you have the __sync functions]) |
||||
|
fi |
||||
|
AC_SUBST(BACKTRACE_SUPPORTS_THREADS) |
||||
|
|
||||
|
# Test for __atomic support. |
||||
|
AC_CACHE_CHECK([__atomic extensions], |
||||
|
[libbacktrace_cv_sys_atomic], |
||||
|
[if test -n "${with_target_subdir}"; then |
||||
|
libbacktrace_cv_sys_atomic=yes |
||||
|
else |
||||
|
AC_LINK_IFELSE( |
||||
|
[AC_LANG_PROGRAM([int i;], |
||||
|
[__atomic_load_n (&i, __ATOMIC_ACQUIRE); |
||||
|
__atomic_store_n (&i, 1, __ATOMIC_RELEASE);])], |
||||
|
[libbacktrace_cv_sys_atomic=yes], |
||||
|
[libbacktrace_cv_sys_atomic=no]) |
||||
|
fi]) |
||||
|
if test "$libbacktrace_cv_sys_atomic" = "yes"; then |
||||
|
AC_DEFINE([HAVE_ATOMIC_FUNCTIONS], 1, |
||||
|
[Define to 1 if you have the __atomic functions]) |
||||
|
fi |
||||
|
|
||||
|
# The library needs to be able to read the executable itself. Compile |
||||
|
# a file to determine the executable format. The awk script |
||||
|
# filetype.awk prints out the file type. |
||||
|
AC_CACHE_CHECK([output filetype], |
||||
|
[libbacktrace_cv_sys_filetype], |
||||
|
[filetype= |
||||
|
AC_COMPILE_IFELSE( |
||||
|
[AC_LANG_PROGRAM([int i;], [int j;])], |
||||
|
[filetype=`${AWK} -f $srcdir/filetype.awk conftest.$ac_objext`], |
||||
|
[AC_MSG_FAILURE([compiler failed])]) |
||||
|
libbacktrace_cv_sys_filetype=$filetype]) |
||||
|
|
||||
|
# Match the file type to decide what files to compile. |
||||
|
FORMAT_FILE= |
||||
|
backtrace_supports_data=yes |
||||
|
case "$libbacktrace_cv_sys_filetype" in |
||||
|
elf*) FORMAT_FILE="elf.lo" ;; |
||||
|
pecoff) FORMAT_FILE="pecoff.lo" |
||||
|
backtrace_supports_data=no |
||||
|
;; |
||||
|
xcoff*) FORMAT_FILE="xcoff.lo" |
||||
|
backtrace_supports_data=no |
||||
|
;; |
||||
|
*) AC_MSG_WARN([could not determine output file type]) |
||||
|
FORMAT_FILE="unknown.lo" |
||||
|
backtrace_supported=no |
||||
|
;; |
||||
|
esac |
||||
|
AC_SUBST(FORMAT_FILE) |
||||
|
|
||||
|
# ELF defines. |
||||
|
elfsize= |
||||
|
case "$libbacktrace_cv_sys_filetype" in |
||||
|
elf32) elfsize=32 ;; |
||||
|
elf64) elfsize=64 ;; |
||||
|
*) elfsize=unused |
||||
|
esac |
||||
|
AC_DEFINE_UNQUOTED([BACKTRACE_ELF_SIZE], [$elfsize], [ELF size: 32 or 64]) |
||||
|
|
||||
|
# XCOFF defines. |
||||
|
xcoffsize= |
||||
|
case "$libbacktrace_cv_sys_filetype" in |
||||
|
xcoff32) xcoffsize=32 ;; |
||||
|
xcoff64) xcoffsize=64 ;; |
||||
|
*) xcoffsize=unused |
||||
|
esac |
||||
|
AC_DEFINE_UNQUOTED([BACKTRACE_XCOFF_SIZE], [$xcoffsize], [XCOFF size: 32 or 64]) |
||||
|
|
||||
|
BACKTRACE_SUPPORTED=0 |
||||
|
if test "$backtrace_supported" = "yes"; then |
||||
|
BACKTRACE_SUPPORTED=1 |
||||
|
fi |
||||
|
AC_SUBST(BACKTRACE_SUPPORTED) |
||||
|
|
||||
|
BACKTRACE_SUPPORTS_DATA=0 |
||||
|
if test "$backtrace_supports_data" = "yes"; then |
||||
|
BACKTRACE_SUPPORTS_DATA=1 |
||||
|
fi |
||||
|
AC_SUBST(BACKTRACE_SUPPORTS_DATA) |
||||
|
|
||||
|
AC_CHECK_HEADERS(sys/mman.h) |
||||
|
if test "$ac_cv_header_sys_mman_h" = "no"; then |
||||
|
have_mmap=no |
||||
|
else |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
# When built as a GCC target library, we can't do a link test. We |
||||
|
# simply assume that if we have mman.h, we have mmap. |
||||
|
have_mmap=yes |
||||
|
case "${host}" in |
||||
|
spu-*-*|*-*-msdosdjgpp) |
||||
|
# The SPU does not have mmap, but it has a sys/mman.h header file |
||||
|
# containing "mmap_eaddr" and the mmap flags, confusing the test. |
||||
|
# DJGPP also has sys/man.h, but no mmap |
||||
|
have_mmap=no ;; |
||||
|
esac |
||||
|
else |
||||
|
AC_CHECK_FUNC(mmap, [have_mmap=yes], [have_mmap=no]) |
||||
|
fi |
||||
|
fi |
||||
|
if test "$have_mmap" = "no"; then |
||||
|
VIEW_FILE=read.lo |
||||
|
ALLOC_FILE=alloc.lo |
||||
|
else |
||||
|
VIEW_FILE=mmapio.lo |
||||
|
AC_PREPROC_IFELSE([ |
||||
|
#include <sys/mman.h> |
||||
|
#if !defined(MAP_ANONYMOUS) && !defined(MAP_ANON) |
||||
|
#error no MAP_ANONYMOUS |
||||
|
#endif |
||||
|
], [ALLOC_FILE=mmap.lo], [ALLOC_FILE=alloc.lo]) |
||||
|
fi |
||||
|
AC_SUBST(VIEW_FILE) |
||||
|
AC_SUBST(ALLOC_FILE) |
||||
|
|
||||
|
BACKTRACE_USES_MALLOC=0 |
||||
|
if test "$ALLOC_FILE" = "alloc.lo"; then |
||||
|
BACKTRACE_USES_MALLOC=1 |
||||
|
fi |
||||
|
AC_SUBST(BACKTRACE_USES_MALLOC) |
||||
|
|
||||
|
# Check for dl_iterate_phdr. |
||||
|
AC_CHECK_HEADERS(link.h) |
||||
|
if test "$ac_cv_header_link_h" = "no"; then |
||||
|
have_dl_iterate_phdr=no |
||||
|
else |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
# When built as a GCC target library, we can't do a link test. |
||||
|
AC_EGREP_HEADER([dl_iterate_phdr], [link.h], [have_dl_iterate_phdr=yes], |
||||
|
[have_dl_iterate_phdr=no]) |
||||
|
case "${host}" in |
||||
|
*-*-solaris2.10*) |
||||
|
# Avoid dl_iterate_phdr on Solaris 10, where it is in the |
||||
|
# header file but is only in -ldl. |
||||
|
have_dl_iterate_phdr=no ;; |
||||
|
esac |
||||
|
else |
||||
|
AC_CHECK_FUNC([dl_iterate_phdr], [have_dl_iterate_phdr=yes], |
||||
|
[have_dl_iterate_phdr=no]) |
||||
|
fi |
||||
|
fi |
||||
|
if test "$have_dl_iterate_phdr" = "yes"; then |
||||
|
AC_DEFINE(HAVE_DL_ITERATE_PHDR, 1, [Define if dl_iterate_phdr is available.]) |
||||
|
fi |
||||
|
|
||||
|
# Check for loadquery. |
||||
|
AC_CHECK_HEADERS(sys/ldr.h) |
||||
|
if test "$ac_cv_header_sys_ldr_h" = "no"; then |
||||
|
have_loadquery=no |
||||
|
else |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
# When built as a GCC target library, we can't do a link test. |
||||
|
AC_EGREP_HEADER([loadquery], [sys/ldr.h], [have_loadquery=yes], |
||||
|
[have_loadquery=no]) |
||||
|
else |
||||
|
AC_CHECK_FUNC([loadquery], [have_loadquery=yes], |
||||
|
[have_loadquery=no]) |
||||
|
fi |
||||
|
fi |
||||
|
if test "$have_loadquery" = "yes"; then |
||||
|
AC_DEFINE(HAVE_LOADQUERY, 1, [Define if AIX loadquery is available.]) |
||||
|
fi |
||||
|
|
||||
|
# Check for the fcntl function. |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
case "${host}" in |
||||
|
*-*-mingw*) have_fcntl=no ;; |
||||
|
spu-*-*) have_fcntl=no ;; |
||||
|
*) have_fcntl=yes ;; |
||||
|
esac |
||||
|
else |
||||
|
AC_CHECK_FUNC(fcntl, [have_fcntl=yes], [have_fcntl=no]) |
||||
|
fi |
||||
|
if test "$have_fcntl" = "yes"; then |
||||
|
AC_DEFINE([HAVE_FCNTL], 1, |
||||
|
[Define to 1 if you have the fcntl function]) |
||||
|
fi |
||||
|
|
||||
|
AC_CHECK_DECLS(strnlen) |
||||
|
AC_CHECK_FUNCS(lstat readlink) |
||||
|
|
||||
|
# Check for getexecname function. |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
case "${host}" in |
||||
|
*-*-solaris2*) have_getexecname=yes ;; |
||||
|
*) have_getexecname=no ;; |
||||
|
esac |
||||
|
else |
||||
|
AC_CHECK_FUNC(getexecname, [have_getexecname=yes], [have_getexecname=no]) |
||||
|
fi |
||||
|
if test "$have_getexecname" = "yes"; then |
||||
|
AC_DEFINE(HAVE_GETEXECNAME, 1, [Define if getexecname is available.]) |
||||
|
fi |
||||
|
|
||||
|
# Check for the clock_gettime function. |
||||
|
AC_CHECK_FUNCS(clock_gettime) |
||||
|
clock_gettime_link= |
||||
|
# At least for glibc, clock_gettime is in librt. But don't |
||||
|
# pull that in if it still doesn't give us the function we want. This |
||||
|
# test is copied from libgomp, and modified to not link in -lrt as |
||||
|
# we're using this for test timing only. |
||||
|
if test "$ac_cv_func_clock_gettime" = no; then |
||||
|
AC_CHECK_LIB(rt, clock_gettime, |
||||
|
[CLOCK_GETTIME_LINK=-lrt |
||||
|
AC_DEFINE(HAVE_CLOCK_GETTIME, 1, |
||||
|
[Define to 1 if you have the `clock_gettime' function.])]) |
||||
|
fi |
||||
|
AC_SUBST(CLOCK_GETTIME_LINK) |
||||
|
|
||||
|
dnl Test whether the compiler supports the -pthread option. |
||||
|
AC_CACHE_CHECK([whether -pthread is supported], |
||||
|
[libgo_cv_lib_pthread], |
||||
|
[CFLAGS_hold=$CFLAGS |
||||
|
CFLAGS="$CFLAGS -pthread" |
||||
|
AC_COMPILE_IFELSE([[int i;]], |
||||
|
[libgo_cv_lib_pthread=yes], |
||||
|
[libgo_cv_lib_pthread=no]) |
||||
|
CFLAGS=$CFLAGS_hold]) |
||||
|
PTHREAD_CFLAGS= |
||||
|
if test "$libgo_cv_lib_pthread" = yes; then |
||||
|
PTHREAD_CFLAGS=-pthread |
||||
|
fi |
||||
|
AC_SUBST(PTHREAD_CFLAGS) |
||||
|
|
||||
|
AM_CONDITIONAL(HAVE_PTHREAD, test "$libgo_cv_lib_pthread" = yes) |
||||
|
|
||||
|
AC_CHECK_LIB([z], [compress], []) |
||||
|
if test $ac_cv_lib_z_compress = "yes"; then |
||||
|
AC_DEFINE(HAVE_ZLIB, 1, [Define if -lz is available.]) |
||||
|
fi |
||||
|
AM_CONDITIONAL(HAVE_ZLIB, test "$ac_cv_lib_z_compress" = yes) |
||||
|
|
||||
|
dnl Test whether the linker supports the --compress_debug_sections option. |
||||
|
AC_CACHE_CHECK([whether --compress-debug-sections is supported], |
||||
|
[libgo_cv_ld_compress], |
||||
|
[LDFLAGS_hold=$LDFLAGS |
||||
|
LDFLAGS="$LDFLAGS -Wl,--compress-debug-sections=zlib-gnu" |
||||
|
AC_LINK_IFELSE([AC_LANG_PROGRAM(,)], |
||||
|
[libgo_cv_ld_compress=yes], |
||||
|
[libgo_cv_ld_compress=no]) |
||||
|
LDFLAGS=$LDFLAGS_hold]) |
||||
|
AM_CONDITIONAL(HAVE_COMPRESSED_DEBUG, test "$libgo_cv_ld_compress" = yes) |
||||
|
|
||||
|
AC_ARG_VAR(OBJCOPY, [location of objcopy]) |
||||
|
AC_CHECK_PROG(OBJCOPY, objcopy, objcopy,) |
||||
|
AC_CACHE_CHECK([whether objcopy supports debuglink], |
||||
|
[libbacktrace_cv_objcopy_debuglink], |
||||
|
[if test -n "${with_target_subdir}"; then |
||||
|
libbacktrace_cv_objcopy_debuglink=no |
||||
|
elif ${OBJCOPY} --add-gnu-debuglink=x /bin/ls /tmp/ls$$; then |
||||
|
rm -f /tmp/ls$$ |
||||
|
libbacktrace_cv_objcopy_debuglink=yes |
||||
|
else |
||||
|
libbacktrace_cv_objcopy_debuglink=no |
||||
|
fi]) |
||||
|
AM_CONDITIONAL(HAVE_OBJCOPY_DEBUGLINK, test "$libbacktrace_cv_objcopy_debuglink" = yes) |
||||
|
|
||||
|
AC_CACHE_CHECK([whether tests can run], |
||||
|
[libbacktrace_cv_sys_native], |
||||
|
[AC_RUN_IFELSE([AC_LANG_PROGRAM([], [return 0;])], |
||||
|
[libbacktrace_cv_sys_native=yes], |
||||
|
[libbacktrace_cv_sys_native=no], |
||||
|
[libbacktrace_cv_sys_native=no])]) |
||||
|
AM_CONDITIONAL(NATIVE, test "$libbacktrace_cv_sys_native" = "yes") |
||||
|
|
||||
|
if test "${multilib}" = "yes"; then |
||||
|
multilib_arg="--enable-multilib" |
||||
|
else |
||||
|
multilib_arg= |
||||
|
fi |
||||
|
|
||||
|
AC_CONFIG_FILES(Makefile backtrace-supported.h) |
||||
|
|
||||
|
# We need multilib support, but only if configuring for the target. |
||||
|
AC_CONFIG_COMMANDS([default], |
||||
|
[if test -n "$CONFIG_FILES"; then |
||||
|
if test -n "${with_target_subdir}"; then |
||||
|
# Multilibs need MULTISUBDIR defined correctly in certain makefiles so |
||||
|
# that multilib installs will end up installed in the correct place. |
||||
|
# The testsuite needs it for multilib-aware ABI baseline files. |
||||
|
# To work around this not being passed down from config-ml.in -> |
||||
|
# srcdir/Makefile.am -> srcdir/{src,libsupc++,...}/Makefile.am, manually |
||||
|
# append it here. Only modify Makefiles that have just been created. |
||||
|
# |
||||
|
# Also, get rid of this simulated-VPATH thing that automake does. |
||||
|
cat > vpsed << \_EOF |
||||
|
s!`test -f '$<' || echo '$(srcdir)/'`!! |
||||
|
_EOF |
||||
|
for i in $SUBDIRS; do |
||||
|
case $CONFIG_FILES in |
||||
|
*${i}/Makefile*) |
||||
|
#echo "Adding MULTISUBDIR to $i/Makefile" |
||||
|
sed -f vpsed $i/Makefile > tmp |
||||
|
grep '^MULTISUBDIR =' Makefile >> tmp |
||||
|
mv tmp $i/Makefile |
||||
|
;; |
||||
|
esac |
||||
|
done |
||||
|
rm vpsed |
||||
|
fi |
||||
|
fi |
||||
|
], |
||||
|
[ |
||||
|
# Variables needed in config.status (file generation) which aren't already |
||||
|
# passed by autoconf. |
||||
|
SUBDIRS="$SUBDIRS" |
||||
|
]) |
||||
|
|
||||
|
AC_OUTPUT |
File diff suppressed because it is too large
@ -0,0 +1,121 @@ |
|||||
|
/* edtest.c -- Test for libbacktrace storage allocation stress handling
|
||||
|
Copyright (C) 2017-2018 Free Software Foundation, Inc. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "backtrace-supported.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
#include "testlib.h" |
||||
|
|
||||
|
static int test1 (void) __attribute__ ((noinline, unused)); |
||||
|
static int test1 (void) __attribute__ ((noinline, unused)); |
||||
|
extern int f2 (int); |
||||
|
extern int f3 (int, int); |
||||
|
|
||||
|
static int |
||||
|
test1 (void) |
||||
|
{ |
||||
|
/* Returning a value here and elsewhere avoids a tailcall which
|
||||
|
would mess up the backtrace. */ |
||||
|
return f2 (__LINE__) + 1; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
f3 (int f1line, int f2line) |
||||
|
{ |
||||
|
struct info all[20]; |
||||
|
struct bdata data; |
||||
|
int f3line; |
||||
|
int i; |
||||
|
|
||||
|
data.all = &all[0]; |
||||
|
data.index = 0; |
||||
|
data.max = 20; |
||||
|
data.failed = 0; |
||||
|
|
||||
|
f3line = __LINE__ + 1; |
||||
|
i = backtrace_full (state, 0, callback_one, error_callback_one, &data); |
||||
|
|
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "test1: unexpected return value %d\n", i); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (data.index < 3) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test1: not enough frames; got %zu, expected at least 3\n", |
||||
|
data.index); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
check ("test1", 0, all, f3line, "f3", "edtest.c", &data.failed); |
||||
|
check ("test1", 1, all, f2line, "f2", "edtest2_build.c", &data.failed); |
||||
|
check ("test1", 2, all, f1line, "test1", "edtest.c", &data.failed); |
||||
|
|
||||
|
printf ("%s: backtrace_full alloc stress\n", data.failed ? "FAIL" : "PASS"); |
||||
|
|
||||
|
if (data.failed) |
||||
|
++failures; |
||||
|
|
||||
|
return failures; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
main (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) |
||||
|
{ |
||||
|
state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, |
||||
|
error_callback_create, NULL); |
||||
|
|
||||
|
// Grab the storage allocation lock prior to doing anything interesting.
|
||||
|
// The intent here is to insure that the backtrace_alloc code is forced
|
||||
|
// to always call mmap() for new memory as opposed to reusing previously
|
||||
|
// allocated memory from the free list. Doing things this way helps
|
||||
|
// simulate what you might see in a multithreaded program in which there
|
||||
|
// are racing calls to the allocator.
|
||||
|
struct backtrace_state *state_internal = |
||||
|
(struct backtrace_state *) state; |
||||
|
state_internal->lock_alloc = 1; |
||||
|
|
||||
|
// Kick off the test
|
||||
|
test1(); |
||||
|
|
||||
|
exit (failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS); |
||||
|
} |
@ -0,0 +1,43 @@ |
|||||
|
/* edtest2.c -- Test for libbacktrace storage allocation stress handling (p2)
|
||||
|
Copyright (C) 2017-2018 Free Software Foundation, Inc. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
/* This file intentionally written without any #include's
|
||||
|
*/ |
||||
|
|
||||
|
extern int f3(int, int); |
||||
|
extern int f2(int); |
||||
|
|
||||
|
int f2(int x) |
||||
|
{ |
||||
|
/* Returning a value here and elsewhere avoids a tailcall which
|
||||
|
would mess up the backtrace. */ |
||||
|
return f3(x, __LINE__) + 3; |
||||
|
} |
File diff suppressed because it is too large
@ -0,0 +1,201 @@ |
|||||
|
/* fileline.c -- Get file and line number information in a backtrace.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <sys/types.h> |
||||
|
#include <sys/stat.h> |
||||
|
#include <errno.h> |
||||
|
#include <fcntl.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
#ifndef HAVE_GETEXECNAME |
||||
|
#define getexecname() NULL |
||||
|
#endif |
||||
|
|
||||
|
/* Initialize the fileline information from the executable. Returns 1
|
||||
|
on success, 0 on failure. */ |
||||
|
|
||||
|
static int |
||||
|
fileline_initialize (struct backtrace_state *state, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
int failed; |
||||
|
fileline fileline_fn; |
||||
|
int pass; |
||||
|
int called_error_callback; |
||||
|
int descriptor; |
||||
|
const char *filename; |
||||
|
char buf[64]; |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
failed = state->fileline_initialization_failed; |
||||
|
else |
||||
|
failed = backtrace_atomic_load_int (&state->fileline_initialization_failed); |
||||
|
|
||||
|
if (failed) |
||||
|
{ |
||||
|
error_callback (data, "failed to read executable information", -1); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
fileline_fn = state->fileline_fn; |
||||
|
else |
||||
|
fileline_fn = backtrace_atomic_load_pointer (&state->fileline_fn); |
||||
|
if (fileline_fn != NULL) |
||||
|
return 1; |
||||
|
|
||||
|
/* We have not initialized the information. Do it now. */ |
||||
|
|
||||
|
descriptor = -1; |
||||
|
called_error_callback = 0; |
||||
|
for (pass = 0; pass < 5; ++pass) |
||||
|
{ |
||||
|
int does_not_exist; |
||||
|
|
||||
|
switch (pass) |
||||
|
{ |
||||
|
case 0: |
||||
|
filename = state->filename; |
||||
|
break; |
||||
|
case 1: |
||||
|
filename = getexecname (); |
||||
|
break; |
||||
|
case 2: |
||||
|
filename = "/proc/self/exe"; |
||||
|
break; |
||||
|
case 3: |
||||
|
filename = "/proc/curproc/file"; |
||||
|
break; |
||||
|
case 4: |
||||
|
snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out", |
||||
|
(long) getpid ()); |
||||
|
filename = buf; |
||||
|
break; |
||||
|
default: |
||||
|
abort (); |
||||
|
} |
||||
|
|
||||
|
if (filename == NULL) |
||||
|
continue; |
||||
|
|
||||
|
descriptor = backtrace_open (filename, error_callback, data, |
||||
|
&does_not_exist); |
||||
|
if (descriptor < 0 && !does_not_exist) |
||||
|
{ |
||||
|
called_error_callback = 1; |
||||
|
break; |
||||
|
} |
||||
|
if (descriptor >= 0) |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
if (descriptor < 0) |
||||
|
{ |
||||
|
if (!called_error_callback) |
||||
|
{ |
||||
|
if (state->filename != NULL) |
||||
|
error_callback (data, state->filename, ENOENT); |
||||
|
else |
||||
|
error_callback (data, |
||||
|
"libbacktrace could not find executable to open", |
||||
|
0); |
||||
|
} |
||||
|
failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (!failed) |
||||
|
{ |
||||
|
if (!backtrace_initialize (state, filename, descriptor, error_callback, |
||||
|
data, &fileline_fn)) |
||||
|
failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (failed) |
||||
|
{ |
||||
|
if (!state->threaded) |
||||
|
state->fileline_initialization_failed = 1; |
||||
|
else |
||||
|
backtrace_atomic_store_int (&state->fileline_initialization_failed, 1); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
state->fileline_fn = fileline_fn; |
||||
|
else |
||||
|
{ |
||||
|
backtrace_atomic_store_pointer (&state->fileline_fn, fileline_fn); |
||||
|
|
||||
|
/* Note that if two threads initialize at once, one of the data
|
||||
|
sets may be leaked. */ |
||||
|
} |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/* Given a PC, find the file name, line number, and function name. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_pcinfo (struct backtrace_state *state, uintptr_t pc, |
||||
|
backtrace_full_callback callback, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
if (!fileline_initialize (state, error_callback, data)) |
||||
|
return 0; |
||||
|
|
||||
|
if (state->fileline_initialization_failed) |
||||
|
return 0; |
||||
|
|
||||
|
return state->fileline_fn (state, pc, callback, error_callback, data); |
||||
|
} |
||||
|
|
||||
|
/* Given a PC, find the symbol for it, and its value. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_syminfo (struct backtrace_state *state, uintptr_t pc, |
||||
|
backtrace_syminfo_callback callback, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
if (!fileline_initialize (state, error_callback, data)) |
||||
|
return 0; |
||||
|
|
||||
|
if (state->fileline_initialization_failed) |
||||
|
return 0; |
||||
|
|
||||
|
state->syminfo_fn (state, pc, callback, error_callback, data); |
||||
|
return 1; |
||||
|
} |
@ -0,0 +1,49 @@ |
|||||
|
/* btest.c -- Filename header for libbacktrace library
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#ifndef GCC_VERSION |
||||
|
# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) |
||||
|
#endif |
||||
|
|
||||
|
#if (GCC_VERSION < 2007) |
||||
|
# define __attribute__(x) |
||||
|
#endif |
||||
|
|
||||
|
#ifndef ATTRIBUTE_UNUSED |
||||
|
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) |
||||
|
#endif |
||||
|
|
||||
|
#if defined(__MSDOS__) || defined(_WIN32) || defined(__OS2__) || defined (__CYGWIN__) |
||||
|
# define IS_DIR_SEPARATOR(c) ((c) == '/' || (c) == '\\') |
||||
|
#else |
||||
|
# define IS_DIR_SEPARATOR(c) ((c) == '/') |
||||
|
#endif |
@ -0,0 +1,5 @@ |
|||||
|
# An awk script to determine the type of a file. |
||||
|
/\177ELF\001/ { if (NR == 1) { print "elf32"; exit } } |
||||
|
/\177ELF\002/ { if (NR == 1) { print "elf64"; exit } } |
||||
|
/\114\001/ { if (NR == 1) { print "pecoff"; exit } } |
||||
|
/\144\206/ { if (NR == 1) { print "pecoff"; exit } } |
@ -0,0 +1,527 @@ |
|||||
|
#!/bin/sh |
||||
|
# install - install a program, script, or datafile |
||||
|
|
||||
|
scriptversion=2011-01-19.21; # UTC |
||||
|
|
||||
|
# This originates from X11R5 (mit/util/scripts/install.sh), which was |
||||
|
# later released in X11R6 (xc/config/util/install.sh) with the |
||||
|
# following copyright and license. |
||||
|
# |
||||
|
# Copyright (C) 1994 X Consortium |
||||
|
# |
||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
|
# of this software and associated documentation files (the "Software"), to |
||||
|
# deal in the Software without restriction, including without limitation the |
||||
|
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
||||
|
# sell copies of the Software, and to permit persons to whom the Software is |
||||
|
# furnished to do so, subject to the following conditions: |
||||
|
# |
||||
|
# The above copyright notice and this permission notice shall be included in |
||||
|
# all copies or substantial portions of the Software. |
||||
|
# |
||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN |
||||
|
# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- |
||||
|
# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
||||
|
# |
||||
|
# Except as contained in this notice, the name of the X Consortium shall not |
||||
|
# be used in advertising or otherwise to promote the sale, use or other deal- |
||||
|
# ings in this Software without prior written authorization from the X Consor- |
||||
|
# tium. |
||||
|
# |
||||
|
# |
||||
|
# FSF changes to this file are in the public domain. |
||||
|
# |
||||
|
# Calling this script install-sh is preferred over install.sh, to prevent |
||||
|
# `make' implicit rules from creating a file called install from it |
||||
|
# when there is no Makefile. |
||||
|
# |
||||
|
# This script is compatible with the BSD install script, but was written |
||||
|
# from scratch. |
||||
|
|
||||
|
nl=' |
||||
|
' |
||||
|
IFS=" "" $nl" |
||||
|
|
||||
|
# set DOITPROG to echo to test this script |
||||
|
|
||||
|
# Don't use :- since 4.3BSD and earlier shells don't like it. |
||||
|
doit=${DOITPROG-} |
||||
|
if test -z "$doit"; then |
||||
|
doit_exec=exec |
||||
|
else |
||||
|
doit_exec=$doit |
||||
|
fi |
||||
|
|
||||
|
# Put in absolute file names if you don't have them in your path; |
||||
|
# or use environment vars. |
||||
|
|
||||
|
chgrpprog=${CHGRPPROG-chgrp} |
||||
|
chmodprog=${CHMODPROG-chmod} |
||||
|
chownprog=${CHOWNPROG-chown} |
||||
|
cmpprog=${CMPPROG-cmp} |
||||
|
cpprog=${CPPROG-cp} |
||||
|
mkdirprog=${MKDIRPROG-mkdir} |
||||
|
mvprog=${MVPROG-mv} |
||||
|
rmprog=${RMPROG-rm} |
||||
|
stripprog=${STRIPPROG-strip} |
||||
|
|
||||
|
posix_glob='?' |
||||
|
initialize_posix_glob=' |
||||
|
test "$posix_glob" != "?" || { |
||||
|
if (set -f) 2>/dev/null; then |
||||
|
posix_glob= |
||||
|
else |
||||
|
posix_glob=: |
||||
|
fi |
||||
|
} |
||||
|
' |
||||
|
|
||||
|
posix_mkdir= |
||||
|
|
||||
|
# Desired mode of installed file. |
||||
|
mode=0755 |
||||
|
|
||||
|
chgrpcmd= |
||||
|
chmodcmd=$chmodprog |
||||
|
chowncmd= |
||||
|
mvcmd=$mvprog |
||||
|
rmcmd="$rmprog -f" |
||||
|
stripcmd= |
||||
|
|
||||
|
src= |
||||
|
dst= |
||||
|
dir_arg= |
||||
|
dst_arg= |
||||
|
|
||||
|
copy_on_change=false |
||||
|
no_target_directory= |
||||
|
|
||||
|
usage="\ |
||||
|
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE |
||||
|
or: $0 [OPTION]... SRCFILES... DIRECTORY |
||||
|
or: $0 [OPTION]... -t DIRECTORY SRCFILES... |
||||
|
or: $0 [OPTION]... -d DIRECTORIES... |
||||
|
|
||||
|
In the 1st form, copy SRCFILE to DSTFILE. |
||||
|
In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. |
||||
|
In the 4th, create DIRECTORIES. |
||||
|
|
||||
|
Options: |
||||
|
--help display this help and exit. |
||||
|
--version display version info and exit. |
||||
|
|
||||
|
-c (ignored) |
||||
|
-C install only if different (preserve the last data modification time) |
||||
|
-d create directories instead of installing files. |
||||
|
-g GROUP $chgrpprog installed files to GROUP. |
||||
|
-m MODE $chmodprog installed files to MODE. |
||||
|
-o USER $chownprog installed files to USER. |
||||
|
-s $stripprog installed files. |
||||
|
-t DIRECTORY install into DIRECTORY. |
||||
|
-T report an error if DSTFILE is a directory. |
||||
|
|
||||
|
Environment variables override the default commands: |
||||
|
CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG |
||||
|
RMPROG STRIPPROG |
||||
|
" |
||||
|
|
||||
|
while test $# -ne 0; do |
||||
|
case $1 in |
||||
|
-c) ;; |
||||
|
|
||||
|
-C) copy_on_change=true;; |
||||
|
|
||||
|
-d) dir_arg=true;; |
||||
|
|
||||
|
-g) chgrpcmd="$chgrpprog $2" |
||||
|
shift;; |
||||
|
|
||||
|
--help) echo "$usage"; exit $?;; |
||||
|
|
||||
|
-m) mode=$2 |
||||
|
case $mode in |
||||
|
*' '* | *' '* | *' |
||||
|
'* | *'*'* | *'?'* | *'['*) |
||||
|
echo "$0: invalid mode: $mode" >&2 |
||||
|
exit 1;; |
||||
|
esac |
||||
|
shift;; |
||||
|
|
||||
|
-o) chowncmd="$chownprog $2" |
||||
|
shift;; |
||||
|
|
||||
|
-s) stripcmd=$stripprog;; |
||||
|
|
||||
|
-t) dst_arg=$2 |
||||
|
# Protect names problematic for `test' and other utilities. |
||||
|
case $dst_arg in |
||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;; |
||||
|
esac |
||||
|
shift;; |
||||
|
|
||||
|
-T) no_target_directory=true;; |
||||
|
|
||||
|
--version) echo "$0 $scriptversion"; exit $?;; |
||||
|
|
||||
|
--) shift |
||||
|
break;; |
||||
|
|
||||
|
-*) echo "$0: invalid option: $1" >&2 |
||||
|
exit 1;; |
||||
|
|
||||
|
*) break;; |
||||
|
esac |
||||
|
shift |
||||
|
done |
||||
|
|
||||
|
if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then |
||||
|
# When -d is used, all remaining arguments are directories to create. |
||||
|
# When -t is used, the destination is already specified. |
||||
|
# Otherwise, the last argument is the destination. Remove it from $@. |
||||
|
for arg |
||||
|
do |
||||
|
if test -n "$dst_arg"; then |
||||
|
# $@ is not empty: it contains at least $arg. |
||||
|
set fnord "$@" "$dst_arg" |
||||
|
shift # fnord |
||||
|
fi |
||||
|
shift # arg |
||||
|
dst_arg=$arg |
||||
|
# Protect names problematic for `test' and other utilities. |
||||
|
case $dst_arg in |
||||
|
-* | [=\(\)!]) dst_arg=./$dst_arg;; |
||||
|
esac |
||||
|
done |
||||
|
fi |
||||
|
|
||||
|
if test $# -eq 0; then |
||||
|
if test -z "$dir_arg"; then |
||||
|
echo "$0: no input file specified." >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
# It's OK to call `install-sh -d' without argument. |
||||
|
# This can happen when creating conditional directories. |
||||
|
exit 0 |
||||
|
fi |
||||
|
|
||||
|
if test -z "$dir_arg"; then |
||||
|
do_exit='(exit $ret); exit $ret' |
||||
|
trap "ret=129; $do_exit" 1 |
||||
|
trap "ret=130; $do_exit" 2 |
||||
|
trap "ret=141; $do_exit" 13 |
||||
|
trap "ret=143; $do_exit" 15 |
||||
|
|
||||
|
# Set umask so as not to create temps with too-generous modes. |
||||
|
# However, 'strip' requires both read and write access to temps. |
||||
|
case $mode in |
||||
|
# Optimize common cases. |
||||
|
*644) cp_umask=133;; |
||||
|
*755) cp_umask=22;; |
||||
|
|
||||
|
*[0-7]) |
||||
|
if test -z "$stripcmd"; then |
||||
|
u_plus_rw= |
||||
|
else |
||||
|
u_plus_rw='% 200' |
||||
|
fi |
||||
|
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; |
||||
|
*) |
||||
|
if test -z "$stripcmd"; then |
||||
|
u_plus_rw= |
||||
|
else |
||||
|
u_plus_rw=,u+rw |
||||
|
fi |
||||
|
cp_umask=$mode$u_plus_rw;; |
||||
|
esac |
||||
|
fi |
||||
|
|
||||
|
for src |
||||
|
do |
||||
|
# Protect names problematic for `test' and other utilities. |
||||
|
case $src in |
||||
|
-* | [=\(\)!]) src=./$src;; |
||||
|
esac |
||||
|
|
||||
|
if test -n "$dir_arg"; then |
||||
|
dst=$src |
||||
|
dstdir=$dst |
||||
|
test -d "$dstdir" |
||||
|
dstdir_status=$? |
||||
|
else |
||||
|
|
||||
|
# Waiting for this to be detected by the "$cpprog $src $dsttmp" command |
||||
|
# might cause directories to be created, which would be especially bad |
||||
|
# if $src (and thus $dsttmp) contains '*'. |
||||
|
if test ! -f "$src" && test ! -d "$src"; then |
||||
|
echo "$0: $src does not exist." >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
if test -z "$dst_arg"; then |
||||
|
echo "$0: no destination specified." >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
dst=$dst_arg |
||||
|
|
||||
|
# If destination is a directory, append the input filename; won't work |
||||
|
# if double slashes aren't ignored. |
||||
|
if test -d "$dst"; then |
||||
|
if test -n "$no_target_directory"; then |
||||
|
echo "$0: $dst_arg: Is a directory" >&2 |
||||
|
exit 1 |
||||
|
fi |
||||
|
dstdir=$dst |
||||
|
dst=$dstdir/`basename "$src"` |
||||
|
dstdir_status=0 |
||||
|
else |
||||
|
# Prefer dirname, but fall back on a substitute if dirname fails. |
||||
|
dstdir=` |
||||
|
(dirname "$dst") 2>/dev/null || |
||||
|
expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ |
||||
|
X"$dst" : 'X\(//\)[^/]' \| \ |
||||
|
X"$dst" : 'X\(//\)$' \| \ |
||||
|
X"$dst" : 'X\(/\)' \| . 2>/dev/null || |
||||
|
echo X"$dst" | |
||||
|
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ |
||||
|
s//\1/ |
||||
|
q |
||||
|
} |
||||
|
/^X\(\/\/\)[^/].*/{ |
||||
|
s//\1/ |
||||
|
q |
||||
|
} |
||||
|
/^X\(\/\/\)$/{ |
||||
|
s//\1/ |
||||
|
q |
||||
|
} |
||||
|
/^X\(\/\).*/{ |
||||
|
s//\1/ |
||||
|
q |
||||
|
} |
||||
|
s/.*/./; q' |
||||
|
` |
||||
|
|
||||
|
test -d "$dstdir" |
||||
|
dstdir_status=$? |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
obsolete_mkdir_used=false |
||||
|
|
||||
|
if test $dstdir_status != 0; then |
||||
|
case $posix_mkdir in |
||||
|
'') |
||||
|
# Create intermediate dirs using mode 755 as modified by the umask. |
||||
|
# This is like FreeBSD 'install' as of 1997-10-28. |
||||
|
umask=`umask` |
||||
|
case $stripcmd.$umask in |
||||
|
# Optimize common cases. |
||||
|
*[2367][2367]) mkdir_umask=$umask;; |
||||
|
.*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; |
||||
|
|
||||
|
*[0-7]) |
||||
|
mkdir_umask=`expr $umask + 22 \ |
||||
|
- $umask % 100 % 40 + $umask % 20 \ |
||||
|
- $umask % 10 % 4 + $umask % 2 |
||||
|
`;; |
||||
|
*) mkdir_umask=$umask,go-w;; |
||||
|
esac |
||||
|
|
||||
|
# With -d, create the new directory with the user-specified mode. |
||||
|
# Otherwise, rely on $mkdir_umask. |
||||
|
if test -n "$dir_arg"; then |
||||
|
mkdir_mode=-m$mode |
||||
|
else |
||||
|
mkdir_mode= |
||||
|
fi |
||||
|
|
||||
|
posix_mkdir=false |
||||
|
case $umask in |
||||
|
*[123567][0-7][0-7]) |
||||
|
# POSIX mkdir -p sets u+wx bits regardless of umask, which |
||||
|
# is incompatible with FreeBSD 'install' when (umask & 300) != 0. |
||||
|
;; |
||||
|
*) |
||||
|
tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ |
||||
|
trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 |
||||
|
|
||||
|
if (umask $mkdir_umask && |
||||
|
exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 |
||||
|
then |
||||
|
if test -z "$dir_arg" || { |
||||
|
# Check for POSIX incompatibilities with -m. |
||||
|
# HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or |
||||
|
# other-writeable bit of parent directory when it shouldn't. |
||||
|
# FreeBSD 6.1 mkdir -m -p sets mode of existing directory. |
||||
|
ls_ld_tmpdir=`ls -ld "$tmpdir"` |
||||
|
case $ls_ld_tmpdir in |
||||
|
d????-?r-*) different_mode=700;; |
||||
|
d????-?--*) different_mode=755;; |
||||
|
*) false;; |
||||
|
esac && |
||||
|
$mkdirprog -m$different_mode -p -- "$tmpdir" && { |
||||
|
ls_ld_tmpdir_1=`ls -ld "$tmpdir"` |
||||
|
test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" |
||||
|
} |
||||
|
} |
||||
|
then posix_mkdir=: |
||||
|
fi |
||||
|
rmdir "$tmpdir/d" "$tmpdir" |
||||
|
else |
||||
|
# Remove any dirs left behind by ancient mkdir implementations. |
||||
|
rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null |
||||
|
fi |
||||
|
trap '' 0;; |
||||
|
esac;; |
||||
|
esac |
||||
|
|
||||
|
if |
||||
|
$posix_mkdir && ( |
||||
|
umask $mkdir_umask && |
||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" |
||||
|
) |
||||
|
then : |
||||
|
else |
||||
|
|
||||
|
# The umask is ridiculous, or mkdir does not conform to POSIX, |
||||
|
# or it failed possibly due to a race condition. Create the |
||||
|
# directory the slow way, step by step, checking for races as we go. |
||||
|
|
||||
|
case $dstdir in |
||||
|
/*) prefix='/';; |
||||
|
[-=\(\)!]*) prefix='./';; |
||||
|
*) prefix='';; |
||||
|
esac |
||||
|
|
||||
|
eval "$initialize_posix_glob" |
||||
|
|
||||
|
oIFS=$IFS |
||||
|
IFS=/ |
||||
|
$posix_glob set -f |
||||
|
set fnord $dstdir |
||||
|
shift |
||||
|
$posix_glob set +f |
||||
|
IFS=$oIFS |
||||
|
|
||||
|
prefixes= |
||||
|
|
||||
|
for d |
||||
|
do |
||||
|
test X"$d" = X && continue |
||||
|
|
||||
|
prefix=$prefix$d |
||||
|
if test -d "$prefix"; then |
||||
|
prefixes= |
||||
|
else |
||||
|
if $posix_mkdir; then |
||||
|
(umask=$mkdir_umask && |
||||
|
$doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break |
||||
|
# Don't fail if two instances are running concurrently. |
||||
|
test -d "$prefix" || exit 1 |
||||
|
else |
||||
|
case $prefix in |
||||
|
*\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; |
||||
|
*) qprefix=$prefix;; |
||||
|
esac |
||||
|
prefixes="$prefixes '$qprefix'" |
||||
|
fi |
||||
|
fi |
||||
|
prefix=$prefix/ |
||||
|
done |
||||
|
|
||||
|
if test -n "$prefixes"; then |
||||
|
# Don't fail if two instances are running concurrently. |
||||
|
(umask $mkdir_umask && |
||||
|
eval "\$doit_exec \$mkdirprog $prefixes") || |
||||
|
test -d "$dstdir" || exit 1 |
||||
|
obsolete_mkdir_used=true |
||||
|
fi |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
if test -n "$dir_arg"; then |
||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dst"; } && |
||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && |
||||
|
{ test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || |
||||
|
test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 |
||||
|
else |
||||
|
|
||||
|
# Make a couple of temp file names in the proper directory. |
||||
|
dsttmp=$dstdir/_inst.$$_ |
||||
|
rmtmp=$dstdir/_rm.$$_ |
||||
|
|
||||
|
# Trap to clean up those temp files at exit. |
||||
|
trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 |
||||
|
|
||||
|
# Copy the file name to the temp name. |
||||
|
(umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && |
||||
|
|
||||
|
# and set any options; do chmod last to preserve setuid bits. |
||||
|
# |
||||
|
# If any of these fail, we abort the whole thing. If we want to |
||||
|
# ignore errors from any of these, just make sure not to ignore |
||||
|
# errors from the above "$doit $cpprog $src $dsttmp" command. |
||||
|
# |
||||
|
{ test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && |
||||
|
{ test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && |
||||
|
{ test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && |
||||
|
{ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && |
||||
|
|
||||
|
# If -C, don't bother to copy if it wouldn't change the file. |
||||
|
if $copy_on_change && |
||||
|
old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && |
||||
|
new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && |
||||
|
|
||||
|
eval "$initialize_posix_glob" && |
||||
|
$posix_glob set -f && |
||||
|
set X $old && old=:$2:$4:$5:$6 && |
||||
|
set X $new && new=:$2:$4:$5:$6 && |
||||
|
$posix_glob set +f && |
||||
|
|
||||
|
test "$old" = "$new" && |
||||
|
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 |
||||
|
then |
||||
|
rm -f "$dsttmp" |
||||
|
else |
||||
|
# Rename the file to the real destination. |
||||
|
$doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || |
||||
|
|
||||
|
# The rename failed, perhaps because mv can't rename something else |
||||
|
# to itself, or perhaps because mv is so ancient that it does not |
||||
|
# support -f. |
||||
|
{ |
||||
|
# Now remove or move aside any old file at destination location. |
||||
|
# We try this two ways since rm can't unlink itself on some |
||||
|
# systems and the destination file might be busy for other |
||||
|
# reasons. In this case, the final cleanup might fail but the new |
||||
|
# file should still install successfully. |
||||
|
{ |
||||
|
test ! -f "$dst" || |
||||
|
$doit $rmcmd -f "$dst" 2>/dev/null || |
||||
|
{ $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && |
||||
|
{ $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } |
||||
|
} || |
||||
|
{ echo "$0: cannot unlink or rename $dst" >&2 |
||||
|
(exit 1); exit 1 |
||||
|
} |
||||
|
} && |
||||
|
|
||||
|
# Now rename the file to the real destination. |
||||
|
$doit $mvcmd "$dsttmp" "$dst" |
||||
|
} |
||||
|
fi || exit 1 |
||||
|
|
||||
|
trap '' 0 |
||||
|
fi |
||||
|
done |
||||
|
|
||||
|
# Local variables: |
||||
|
# eval: (add-hook 'write-file-hooks 'time-stamp) |
||||
|
# time-stamp-start: "scriptversion=" |
||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H" |
||||
|
# time-stamp-time-zone: "UTC" |
||||
|
# time-stamp-end: "; # UTC" |
||||
|
# End: |
@ -0,0 +1,304 @@ |
|||||
|
/* internal.h -- Internal header file for stack backtrace library.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#ifndef BACKTRACE_INTERNAL_H |
||||
|
#define BACKTRACE_INTERNAL_H |
||||
|
|
||||
|
/* We assume that <sys/types.h> and "backtrace.h" have already been
|
||||
|
included. */ |
||||
|
|
||||
|
#ifndef GCC_VERSION |
||||
|
# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) |
||||
|
#endif |
||||
|
|
||||
|
#if (GCC_VERSION < 2007) |
||||
|
# define __attribute__(x) |
||||
|
#endif |
||||
|
|
||||
|
#ifndef ATTRIBUTE_UNUSED |
||||
|
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) |
||||
|
#endif |
||||
|
|
||||
|
#ifndef ATTRIBUTE_MALLOC |
||||
|
# if (GCC_VERSION >= 2096) |
||||
|
# define ATTRIBUTE_MALLOC __attribute__ ((__malloc__)) |
||||
|
# else |
||||
|
# define ATTRIBUTE_MALLOC |
||||
|
# endif |
||||
|
#endif |
||||
|
|
||||
|
#ifndef HAVE_SYNC_FUNCTIONS |
||||
|
|
||||
|
/* Define out the sync functions. These should never be called if
|
||||
|
they are not available. */ |
||||
|
|
||||
|
#define __sync_bool_compare_and_swap(A, B, C) (abort(), 1) |
||||
|
#define __sync_lock_test_and_set(A, B) (abort(), 0) |
||||
|
#define __sync_lock_release(A) abort() |
||||
|
|
||||
|
#endif /* !defined (HAVE_SYNC_FUNCTIONS) */ |
||||
|
|
||||
|
#ifdef HAVE_ATOMIC_FUNCTIONS |
||||
|
|
||||
|
/* We have the atomic builtin functions. */ |
||||
|
|
||||
|
#define backtrace_atomic_load_pointer(p) \ |
||||
|
__atomic_load_n ((p), __ATOMIC_ACQUIRE) |
||||
|
#define backtrace_atomic_load_int(p) \ |
||||
|
__atomic_load_n ((p), __ATOMIC_ACQUIRE) |
||||
|
#define backtrace_atomic_store_pointer(p, v) \ |
||||
|
__atomic_store_n ((p), (v), __ATOMIC_RELEASE) |
||||
|
#define backtrace_atomic_store_size_t(p, v) \ |
||||
|
__atomic_store_n ((p), (v), __ATOMIC_RELEASE) |
||||
|
#define backtrace_atomic_store_int(p, v) \ |
||||
|
__atomic_store_n ((p), (v), __ATOMIC_RELEASE) |
||||
|
|
||||
|
#else /* !defined (HAVE_ATOMIC_FUNCTIONS) */ |
||||
|
#ifdef HAVE_SYNC_FUNCTIONS |
||||
|
|
||||
|
/* We have the sync functions but not the atomic functions. Define
|
||||
|
the atomic ones in terms of the sync ones. */ |
||||
|
|
||||
|
extern void *backtrace_atomic_load_pointer (void *); |
||||
|
extern int backtrace_atomic_load_int (int *); |
||||
|
extern void backtrace_atomic_store_pointer (void *, void *); |
||||
|
extern void backtrace_atomic_store_size_t (size_t *, size_t); |
||||
|
extern void backtrace_atomic_store_int (int *, int); |
||||
|
|
||||
|
#else /* !defined (HAVE_SYNC_FUNCTIONS) */ |
||||
|
|
||||
|
/* We have neither the sync nor the atomic functions. These will
|
||||
|
never be called. */ |
||||
|
|
||||
|
#define backtrace_atomic_load_pointer(p) (abort(), (void *) NULL) |
||||
|
#define backtrace_atomic_load_int(p) (abort(), 0) |
||||
|
#define backtrace_atomic_store_pointer(p, v) abort() |
||||
|
#define backtrace_atomic_store_size_t(p, v) abort() |
||||
|
#define backtrace_atomic_store_int(p, v) abort() |
||||
|
|
||||
|
#endif /* !defined (HAVE_SYNC_FUNCTIONS) */ |
||||
|
#endif /* !defined (HAVE_ATOMIC_FUNCTIONS) */ |
||||
|
|
||||
|
/* The type of the function that collects file/line information. This
|
||||
|
is like backtrace_pcinfo. */ |
||||
|
|
||||
|
typedef int (*fileline) (struct backtrace_state *state, uintptr_t pc, |
||||
|
backtrace_full_callback callback, |
||||
|
backtrace_error_callback error_callback, void *data); |
||||
|
|
||||
|
/* The type of the function that collects symbol information. This is
|
||||
|
like backtrace_syminfo. */ |
||||
|
|
||||
|
typedef void (*syminfo) (struct backtrace_state *state, uintptr_t pc, |
||||
|
backtrace_syminfo_callback callback, |
||||
|
backtrace_error_callback error_callback, void *data); |
||||
|
|
||||
|
/* What the backtrace state pointer points to. */ |
||||
|
|
||||
|
struct backtrace_state |
||||
|
{ |
||||
|
/* The name of the executable. */ |
||||
|
const char *filename; |
||||
|
/* Non-zero if threaded. */ |
||||
|
int threaded; |
||||
|
/* The master lock for fileline_fn, fileline_data, syminfo_fn,
|
||||
|
syminfo_data, fileline_initialization_failed and everything the |
||||
|
data pointers point to. */ |
||||
|
void *lock; |
||||
|
/* The function that returns file/line information. */ |
||||
|
fileline fileline_fn; |
||||
|
/* The data to pass to FILELINE_FN. */ |
||||
|
void *fileline_data; |
||||
|
/* The function that returns symbol information. */ |
||||
|
syminfo syminfo_fn; |
||||
|
/* The data to pass to SYMINFO_FN. */ |
||||
|
void *syminfo_data; |
||||
|
/* Whether initializing the file/line information failed. */ |
||||
|
int fileline_initialization_failed; |
||||
|
/* The lock for the freelist. */ |
||||
|
int lock_alloc; |
||||
|
/* The freelist when using mmap. */ |
||||
|
struct backtrace_freelist_struct *freelist; |
||||
|
}; |
||||
|
|
||||
|
/* Open a file for reading. Returns -1 on error. If DOES_NOT_EXIST
|
||||
|
is not NULL, *DOES_NOT_EXIST will be set to 0 normally and set to 1 |
||||
|
if the file does not exist. If the file does not exist and |
||||
|
DOES_NOT_EXIST is not NULL, the function will return -1 and will |
||||
|
not call ERROR_CALLBACK. On other errors, or if DOES_NOT_EXIST is |
||||
|
NULL, the function will call ERROR_CALLBACK before returning. */ |
||||
|
extern int backtrace_open (const char *filename, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, |
||||
|
int *does_not_exist); |
||||
|
|
||||
|
/* A view of the contents of a file. This supports mmap when
|
||||
|
available. A view will remain in memory even after backtrace_close |
||||
|
is called on the file descriptor from which the view was |
||||
|
obtained. */ |
||||
|
|
||||
|
struct backtrace_view |
||||
|
{ |
||||
|
/* The data that the caller requested. */ |
||||
|
const void *data; |
||||
|
/* The base of the view. */ |
||||
|
void *base; |
||||
|
/* The total length of the view. */ |
||||
|
size_t len; |
||||
|
}; |
||||
|
|
||||
|
/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. Store the
|
||||
|
result in *VIEW. Returns 1 on success, 0 on error. */ |
||||
|
extern int backtrace_get_view (struct backtrace_state *state, int descriptor, |
||||
|
off_t offset, size_t size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, struct backtrace_view *view); |
||||
|
|
||||
|
/* Release a view created by backtrace_get_view. */ |
||||
|
extern void backtrace_release_view (struct backtrace_state *state, |
||||
|
struct backtrace_view *view, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* Close a file opened by backtrace_open. Returns 1 on success, 0 on
|
||||
|
error. */ |
||||
|
|
||||
|
extern int backtrace_close (int descriptor, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* Sort without using memory. */ |
||||
|
|
||||
|
extern void backtrace_qsort (void *base, size_t count, size_t size, |
||||
|
int (*compar) (const void *, const void *)); |
||||
|
|
||||
|
/* Allocate memory. This is like malloc. If ERROR_CALLBACK is NULL,
|
||||
|
this does not report an error, it just returns NULL. */ |
||||
|
|
||||
|
extern void *backtrace_alloc (struct backtrace_state *state, size_t size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data) ATTRIBUTE_MALLOC; |
||||
|
|
||||
|
/* Free memory allocated by backtrace_alloc. If ERROR_CALLBACK is
|
||||
|
NULL, this does not report an error. */ |
||||
|
|
||||
|
extern void backtrace_free (struct backtrace_state *state, void *mem, |
||||
|
size_t size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* A growable vector of some struct. This is used for more efficient
|
||||
|
allocation when we don't know the final size of some group of data |
||||
|
that we want to represent as an array. */ |
||||
|
|
||||
|
struct backtrace_vector |
||||
|
{ |
||||
|
/* The base of the vector. */ |
||||
|
void *base; |
||||
|
/* The number of bytes in the vector. */ |
||||
|
size_t size; |
||||
|
/* The number of bytes available at the current allocation. */ |
||||
|
size_t alc; |
||||
|
}; |
||||
|
|
||||
|
/* Grow VEC by SIZE bytes. Return a pointer to the newly allocated
|
||||
|
bytes. Note that this may move the entire vector to a new memory |
||||
|
location. Returns NULL on failure. */ |
||||
|
|
||||
|
extern void *backtrace_vector_grow (struct backtrace_state *state, size_t size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, |
||||
|
struct backtrace_vector *vec); |
||||
|
|
||||
|
/* Finish the current allocation on VEC. Prepare to start a new
|
||||
|
allocation. The finished allocation will never be freed. Returns |
||||
|
a pointer to the base of the finished entries, or NULL on |
||||
|
failure. */ |
||||
|
|
||||
|
extern void* backtrace_vector_finish (struct backtrace_state *state, |
||||
|
struct backtrace_vector *vec, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* Release any extra space allocated for VEC. This may change
|
||||
|
VEC->base. Returns 1 on success, 0 on failure. */ |
||||
|
|
||||
|
extern int backtrace_vector_release (struct backtrace_state *state, |
||||
|
struct backtrace_vector *vec, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data); |
||||
|
|
||||
|
/* Read initial debug data from a descriptor, and set the
|
||||
|
fileline_data, syminfo_fn, and syminfo_data fields of STATE. |
||||
|
Return the fileln_fn field in *FILELN_FN--this is done this way so |
||||
|
that the synchronization code is only implemented once. This is |
||||
|
called after the descriptor has first been opened. It will close |
||||
|
the descriptor if it is no longer needed. Returns 1 on success, 0 |
||||
|
on error. There will be multiple implementations of this function, |
||||
|
for different file formats. Each system will compile the |
||||
|
appropriate one. */ |
||||
|
|
||||
|
extern int backtrace_initialize (struct backtrace_state *state, |
||||
|
const char *filename, |
||||
|
int descriptor, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, |
||||
|
fileline *fileline_fn); |
||||
|
|
||||
|
/* Add file/line information for a DWARF module. */ |
||||
|
|
||||
|
extern int backtrace_dwarf_add (struct backtrace_state *state, |
||||
|
uintptr_t base_address, |
||||
|
const unsigned char* dwarf_info, |
||||
|
size_t dwarf_info_size, |
||||
|
const unsigned char *dwarf_line, |
||||
|
size_t dwarf_line_size, |
||||
|
const unsigned char *dwarf_abbrev, |
||||
|
size_t dwarf_abbrev_size, |
||||
|
const unsigned char *dwarf_ranges, |
||||
|
size_t dwarf_range_size, |
||||
|
const unsigned char *dwarf_str, |
||||
|
size_t dwarf_str_size, |
||||
|
int is_bigendian, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, fileline *fileline_fn); |
||||
|
|
||||
|
/* A test-only hook for elf_uncompress_zdebug. */ |
||||
|
|
||||
|
extern int backtrace_uncompress_zdebug (struct backtrace_state *, |
||||
|
const unsigned char *compressed, |
||||
|
size_t compressed_size, |
||||
|
backtrace_error_callback, void *data, |
||||
|
unsigned char **uncompressed, |
||||
|
size_t *uncompressed_size); |
||||
|
|
||||
|
#endif |
File diff suppressed because it is too large
@ -0,0 +1,331 @@ |
|||||
|
#! /bin/sh |
||||
|
# Common stub for a few missing GNU programs while installing. |
||||
|
|
||||
|
scriptversion=2012-01-06.13; # UTC |
||||
|
|
||||
|
# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, |
||||
|
# 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. |
||||
|
# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. |
||||
|
|
||||
|
# This program is free software; you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation; either version 2, or (at your option) |
||||
|
# any later version. |
||||
|
|
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
|
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
# As a special exception to the GNU General Public License, if you |
||||
|
# distribute this file as part of a program that contains a |
||||
|
# configuration script generated by Autoconf, you may include it under |
||||
|
# the same distribution terms that you use for the rest of that program. |
||||
|
|
||||
|
if test $# -eq 0; then |
||||
|
echo 1>&2 "Try \`$0 --help' for more information" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
run=: |
||||
|
sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' |
||||
|
sed_minuso='s/.* -o \([^ ]*\).*/\1/p' |
||||
|
|
||||
|
# In the cases where this matters, `missing' is being run in the |
||||
|
# srcdir already. |
||||
|
if test -f configure.ac; then |
||||
|
configure_ac=configure.ac |
||||
|
else |
||||
|
configure_ac=configure.in |
||||
|
fi |
||||
|
|
||||
|
msg="missing on your system" |
||||
|
|
||||
|
case $1 in |
||||
|
--run) |
||||
|
# Try to run requested program, and just exit if it succeeds. |
||||
|
run= |
||||
|
shift |
||||
|
"$@" && exit 0 |
||||
|
# Exit code 63 means version mismatch. This often happens |
||||
|
# when the user try to use an ancient version of a tool on |
||||
|
# a file that requires a minimum version. In this case we |
||||
|
# we should proceed has if the program had been absent, or |
||||
|
# if --run hadn't been passed. |
||||
|
if test $? = 63; then |
||||
|
run=: |
||||
|
msg="probably too old" |
||||
|
fi |
||||
|
;; |
||||
|
|
||||
|
-h|--h|--he|--hel|--help) |
||||
|
echo "\ |
||||
|
$0 [OPTION]... PROGRAM [ARGUMENT]... |
||||
|
|
||||
|
Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an |
||||
|
error status if there is no known handling for PROGRAM. |
||||
|
|
||||
|
Options: |
||||
|
-h, --help display this help and exit |
||||
|
-v, --version output version information and exit |
||||
|
--run try to run the given command, and emulate it if it fails |
||||
|
|
||||
|
Supported PROGRAM values: |
||||
|
aclocal touch file \`aclocal.m4' |
||||
|
autoconf touch file \`configure' |
||||
|
autoheader touch file \`config.h.in' |
||||
|
autom4te touch the output file, or create a stub one |
||||
|
automake touch all \`Makefile.in' files |
||||
|
bison create \`y.tab.[ch]', if possible, from existing .[ch] |
||||
|
flex create \`lex.yy.c', if possible, from existing .c |
||||
|
help2man touch the output file |
||||
|
lex create \`lex.yy.c', if possible, from existing .c |
||||
|
makeinfo touch the output file |
||||
|
yacc create \`y.tab.[ch]', if possible, from existing .[ch] |
||||
|
|
||||
|
Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and |
||||
|
\`g' are ignored when checking the name. |
||||
|
|
||||
|
Send bug reports to <bug-automake@gnu.org>." |
||||
|
exit $? |
||||
|
;; |
||||
|
|
||||
|
-v|--v|--ve|--ver|--vers|--versi|--versio|--version) |
||||
|
echo "missing $scriptversion (GNU Automake)" |
||||
|
exit $? |
||||
|
;; |
||||
|
|
||||
|
-*) |
||||
|
echo 1>&2 "$0: Unknown \`$1' option" |
||||
|
echo 1>&2 "Try \`$0 --help' for more information" |
||||
|
exit 1 |
||||
|
;; |
||||
|
|
||||
|
esac |
||||
|
|
||||
|
# normalize program name to check for. |
||||
|
program=`echo "$1" | sed ' |
||||
|
s/^gnu-//; t |
||||
|
s/^gnu//; t |
||||
|
s/^g//; t'` |
||||
|
|
||||
|
# Now exit if we have it, but it failed. Also exit now if we |
||||
|
# don't have it and --version was passed (most likely to detect |
||||
|
# the program). This is about non-GNU programs, so use $1 not |
||||
|
# $program. |
||||
|
case $1 in |
||||
|
lex*|yacc*) |
||||
|
# Not GNU programs, they don't have --version. |
||||
|
;; |
||||
|
|
||||
|
*) |
||||
|
if test -z "$run" && ($1 --version) > /dev/null 2>&1; then |
||||
|
# We have it, but it failed. |
||||
|
exit 1 |
||||
|
elif test "x$2" = "x--version" || test "x$2" = "x--help"; then |
||||
|
# Could not run --version or --help. This is probably someone |
||||
|
# running `$TOOL --version' or `$TOOL --help' to check whether |
||||
|
# $TOOL exists and not knowing $TOOL uses missing. |
||||
|
exit 1 |
||||
|
fi |
||||
|
;; |
||||
|
esac |
||||
|
|
||||
|
# If it does not exist, or fails to run (possibly an outdated version), |
||||
|
# try to emulate it. |
||||
|
case $program in |
||||
|
aclocal*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is $msg. You should only need it if |
||||
|
you modified \`acinclude.m4' or \`${configure_ac}'. You might want |
||||
|
to install the \`Automake' and \`Perl' packages. Grab them from |
||||
|
any GNU archive site." |
||||
|
touch aclocal.m4 |
||||
|
;; |
||||
|
|
||||
|
autoconf*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is $msg. You should only need it if |
||||
|
you modified \`${configure_ac}'. You might want to install the |
||||
|
\`Autoconf' and \`GNU m4' packages. Grab them from any GNU |
||||
|
archive site." |
||||
|
touch configure |
||||
|
;; |
||||
|
|
||||
|
autoheader*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is $msg. You should only need it if |
||||
|
you modified \`acconfig.h' or \`${configure_ac}'. You might want |
||||
|
to install the \`Autoconf' and \`GNU m4' packages. Grab them |
||||
|
from any GNU archive site." |
||||
|
files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` |
||||
|
test -z "$files" && files="config.h" |
||||
|
touch_files= |
||||
|
for f in $files; do |
||||
|
case $f in |
||||
|
*:*) touch_files="$touch_files "`echo "$f" | |
||||
|
sed -e 's/^[^:]*://' -e 's/:.*//'`;; |
||||
|
*) touch_files="$touch_files $f.in";; |
||||
|
esac |
||||
|
done |
||||
|
touch $touch_files |
||||
|
;; |
||||
|
|
||||
|
automake*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is $msg. You should only need it if |
||||
|
you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. |
||||
|
You might want to install the \`Automake' and \`Perl' packages. |
||||
|
Grab them from any GNU archive site." |
||||
|
find . -type f -name Makefile.am -print | |
||||
|
sed 's/\.am$/.in/' | |
||||
|
while read f; do touch "$f"; done |
||||
|
;; |
||||
|
|
||||
|
autom4te*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is needed, but is $msg. |
||||
|
You might have modified some files without having the |
||||
|
proper tools for further handling them. |
||||
|
You can get \`$1' as part of \`Autoconf' from any GNU |
||||
|
archive site." |
||||
|
|
||||
|
file=`echo "$*" | sed -n "$sed_output"` |
||||
|
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` |
||||
|
if test -f "$file"; then |
||||
|
touch $file |
||||
|
else |
||||
|
test -z "$file" || exec >$file |
||||
|
echo "#! /bin/sh" |
||||
|
echo "# Created by GNU Automake missing as a replacement of" |
||||
|
echo "# $ $@" |
||||
|
echo "exit 0" |
||||
|
chmod +x $file |
||||
|
exit 1 |
||||
|
fi |
||||
|
;; |
||||
|
|
||||
|
bison*|yacc*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' $msg. You should only need it if |
||||
|
you modified a \`.y' file. You may need the \`Bison' package |
||||
|
in order for those modifications to take effect. You can get |
||||
|
\`Bison' from any GNU archive site." |
||||
|
rm -f y.tab.c y.tab.h |
||||
|
if test $# -ne 1; then |
||||
|
eval LASTARG=\${$#} |
||||
|
case $LASTARG in |
||||
|
*.y) |
||||
|
SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` |
||||
|
if test -f "$SRCFILE"; then |
||||
|
cp "$SRCFILE" y.tab.c |
||||
|
fi |
||||
|
SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` |
||||
|
if test -f "$SRCFILE"; then |
||||
|
cp "$SRCFILE" y.tab.h |
||||
|
fi |
||||
|
;; |
||||
|
esac |
||||
|
fi |
||||
|
if test ! -f y.tab.h; then |
||||
|
echo >y.tab.h |
||||
|
fi |
||||
|
if test ! -f y.tab.c; then |
||||
|
echo 'main() { return 0; }' >y.tab.c |
||||
|
fi |
||||
|
;; |
||||
|
|
||||
|
lex*|flex*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is $msg. You should only need it if |
||||
|
you modified a \`.l' file. You may need the \`Flex' package |
||||
|
in order for those modifications to take effect. You can get |
||||
|
\`Flex' from any GNU archive site." |
||||
|
rm -f lex.yy.c |
||||
|
if test $# -ne 1; then |
||||
|
eval LASTARG=\${$#} |
||||
|
case $LASTARG in |
||||
|
*.l) |
||||
|
SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` |
||||
|
if test -f "$SRCFILE"; then |
||||
|
cp "$SRCFILE" lex.yy.c |
||||
|
fi |
||||
|
;; |
||||
|
esac |
||||
|
fi |
||||
|
if test ! -f lex.yy.c; then |
||||
|
echo 'main() { return 0; }' >lex.yy.c |
||||
|
fi |
||||
|
;; |
||||
|
|
||||
|
help2man*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is $msg. You should only need it if |
||||
|
you modified a dependency of a manual page. You may need the |
||||
|
\`Help2man' package in order for those modifications to take |
||||
|
effect. You can get \`Help2man' from any GNU archive site." |
||||
|
|
||||
|
file=`echo "$*" | sed -n "$sed_output"` |
||||
|
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` |
||||
|
if test -f "$file"; then |
||||
|
touch $file |
||||
|
else |
||||
|
test -z "$file" || exec >$file |
||||
|
echo ".ab help2man is required to generate this page" |
||||
|
exit $? |
||||
|
fi |
||||
|
;; |
||||
|
|
||||
|
makeinfo*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is $msg. You should only need it if |
||||
|
you modified a \`.texi' or \`.texinfo' file, or any other file |
||||
|
indirectly affecting the aspect of the manual. The spurious |
||||
|
call might also be the consequence of using a buggy \`make' (AIX, |
||||
|
DU, IRIX). You might want to install the \`Texinfo' package or |
||||
|
the \`GNU make' package. Grab either from any GNU archive site." |
||||
|
# The file to touch is that specified with -o ... |
||||
|
file=`echo "$*" | sed -n "$sed_output"` |
||||
|
test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` |
||||
|
if test -z "$file"; then |
||||
|
# ... or it is the one specified with @setfilename ... |
||||
|
infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` |
||||
|
file=`sed -n ' |
||||
|
/^@setfilename/{ |
||||
|
s/.* \([^ ]*\) *$/\1/ |
||||
|
p |
||||
|
q |
||||
|
}' $infile` |
||||
|
# ... or it is derived from the source name (dir/f.texi becomes f.info) |
||||
|
test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info |
||||
|
fi |
||||
|
# If the file does not exist, the user really needs makeinfo; |
||||
|
# let's fail without touching anything. |
||||
|
test -f $file || exit 1 |
||||
|
touch $file |
||||
|
;; |
||||
|
|
||||
|
*) |
||||
|
echo 1>&2 "\ |
||||
|
WARNING: \`$1' is needed, and is $msg. |
||||
|
You might have modified some files without having the |
||||
|
proper tools for further handling them. Check the \`README' file, |
||||
|
it often tells you about the needed prerequisites for installing |
||||
|
this package. You may also peek at any GNU archive site, in case |
||||
|
some other package would contain this missing \`$1' program." |
||||
|
exit 1 |
||||
|
;; |
||||
|
esac |
||||
|
|
||||
|
exit 0 |
||||
|
|
||||
|
# Local variables: |
||||
|
# eval: (add-hook 'write-file-hooks 'time-stamp) |
||||
|
# time-stamp-start: "scriptversion=" |
||||
|
# time-stamp-format: "%:y-%02m-%02d.%02H" |
||||
|
# time-stamp-time-zone: "UTC" |
||||
|
# time-stamp-end: "; # UTC" |
||||
|
# End: |
@ -0,0 +1,325 @@ |
|||||
|
/* mmap.c -- Memory allocation with mmap.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <string.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <unistd.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/mman.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* Memory allocation on systems that provide anonymous mmap. This
|
||||
|
permits the backtrace functions to be invoked from a signal |
||||
|
handler, assuming that mmap is async-signal safe. */ |
||||
|
|
||||
|
#ifndef MAP_ANONYMOUS |
||||
|
#define MAP_ANONYMOUS MAP_ANON |
||||
|
#endif |
||||
|
|
||||
|
#ifndef MAP_FAILED |
||||
|
#define MAP_FAILED ((void *)-1) |
||||
|
#endif |
||||
|
|
||||
|
/* A list of free memory blocks. */ |
||||
|
|
||||
|
struct backtrace_freelist_struct |
||||
|
{ |
||||
|
/* Next on list. */ |
||||
|
struct backtrace_freelist_struct *next; |
||||
|
/* Size of this block, including this structure. */ |
||||
|
size_t size; |
||||
|
}; |
||||
|
|
||||
|
/* Free memory allocated by backtrace_alloc. */ |
||||
|
|
||||
|
static void |
||||
|
backtrace_free_locked (struct backtrace_state *state, void *addr, size_t size) |
||||
|
{ |
||||
|
/* Just leak small blocks. We don't have to be perfect. Don't put
|
||||
|
more than 16 entries on the free list, to avoid wasting time |
||||
|
searching when allocating a block. If we have more than 16 |
||||
|
entries, leak the smallest entry. */ |
||||
|
|
||||
|
if (size >= sizeof (struct backtrace_freelist_struct)) |
||||
|
{ |
||||
|
size_t c; |
||||
|
struct backtrace_freelist_struct **ppsmall; |
||||
|
struct backtrace_freelist_struct **pp; |
||||
|
struct backtrace_freelist_struct *p; |
||||
|
|
||||
|
c = 0; |
||||
|
ppsmall = NULL; |
||||
|
for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) |
||||
|
{ |
||||
|
if (ppsmall == NULL || (*pp)->size < (*ppsmall)->size) |
||||
|
ppsmall = pp; |
||||
|
++c; |
||||
|
} |
||||
|
if (c >= 16) |
||||
|
{ |
||||
|
if (size <= (*ppsmall)->size) |
||||
|
return; |
||||
|
*ppsmall = (*ppsmall)->next; |
||||
|
} |
||||
|
|
||||
|
p = (struct backtrace_freelist_struct *) addr; |
||||
|
p->next = state->freelist; |
||||
|
p->size = size; |
||||
|
state->freelist = p; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Allocate memory like malloc. If ERROR_CALLBACK is NULL, don't
|
||||
|
report an error. */ |
||||
|
|
||||
|
void * |
||||
|
backtrace_alloc (struct backtrace_state *state, |
||||
|
size_t size, backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
void *ret; |
||||
|
int locked; |
||||
|
struct backtrace_freelist_struct **pp; |
||||
|
size_t pagesize; |
||||
|
size_t asksize; |
||||
|
void *page; |
||||
|
|
||||
|
ret = NULL; |
||||
|
|
||||
|
/* If we can acquire the lock, then see if there is space on the
|
||||
|
free list. If we can't acquire the lock, drop straight into |
||||
|
using mmap. __sync_lock_test_and_set returns the old state of |
||||
|
the lock, so we have acquired it if it returns 0. */ |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
locked = 1; |
||||
|
else |
||||
|
locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0; |
||||
|
|
||||
|
if (locked) |
||||
|
{ |
||||
|
for (pp = &state->freelist; *pp != NULL; pp = &(*pp)->next) |
||||
|
{ |
||||
|
if ((*pp)->size >= size) |
||||
|
{ |
||||
|
struct backtrace_freelist_struct *p; |
||||
|
|
||||
|
p = *pp; |
||||
|
*pp = p->next; |
||||
|
|
||||
|
/* Round for alignment; we assume that no type we care about
|
||||
|
is more than 8 bytes. */ |
||||
|
size = (size + 7) & ~ (size_t) 7; |
||||
|
if (size < p->size) |
||||
|
backtrace_free_locked (state, (char *) p + size, |
||||
|
p->size - size); |
||||
|
|
||||
|
ret = (void *) p; |
||||
|
|
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (state->threaded) |
||||
|
__sync_lock_release (&state->lock_alloc); |
||||
|
} |
||||
|
|
||||
|
if (ret == NULL) |
||||
|
{ |
||||
|
/* Allocate a new page. */ |
||||
|
|
||||
|
pagesize = getpagesize (); |
||||
|
asksize = (size + pagesize - 1) & ~ (pagesize - 1); |
||||
|
page = mmap (NULL, asksize, PROT_READ | PROT_WRITE, |
||||
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
||||
|
if (page == MAP_FAILED) |
||||
|
{ |
||||
|
if (error_callback) |
||||
|
error_callback (data, "mmap", errno); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
size = (size + 7) & ~ (size_t) 7; |
||||
|
if (size < asksize) |
||||
|
backtrace_free (state, (char *) page + size, asksize - size, |
||||
|
error_callback, data); |
||||
|
|
||||
|
ret = page; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/* Free memory allocated by backtrace_alloc. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_free (struct backtrace_state *state, void *addr, size_t size, |
||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
||||
|
void *data ATTRIBUTE_UNUSED) |
||||
|
{ |
||||
|
int locked; |
||||
|
|
||||
|
/* If we are freeing a large aligned block, just release it back to
|
||||
|
the system. This case arises when growing a vector for a large |
||||
|
binary with lots of debug info. Calling munmap here may cause us |
||||
|
to call mmap again if there is also a large shared library; we |
||||
|
just live with that. */ |
||||
|
if (size >= 16 * 4096) |
||||
|
{ |
||||
|
size_t pagesize; |
||||
|
|
||||
|
pagesize = getpagesize (); |
||||
|
if (((uintptr_t) addr & (pagesize - 1)) == 0 |
||||
|
&& (size & (pagesize - 1)) == 0) |
||||
|
{ |
||||
|
/* If munmap fails for some reason, just add the block to
|
||||
|
the freelist. */ |
||||
|
if (munmap (addr, size) == 0) |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* If we can acquire the lock, add the new space to the free list.
|
||||
|
If we can't acquire the lock, just leak the memory. |
||||
|
__sync_lock_test_and_set returns the old state of the lock, so we |
||||
|
have acquired it if it returns 0. */ |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
locked = 1; |
||||
|
else |
||||
|
locked = __sync_lock_test_and_set (&state->lock_alloc, 1) == 0; |
||||
|
|
||||
|
if (locked) |
||||
|
{ |
||||
|
backtrace_free_locked (state, addr, size); |
||||
|
|
||||
|
if (state->threaded) |
||||
|
__sync_lock_release (&state->lock_alloc); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Grow VEC by SIZE bytes. */ |
||||
|
|
||||
|
void * |
||||
|
backtrace_vector_grow (struct backtrace_state *state,size_t size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, struct backtrace_vector *vec) |
||||
|
{ |
||||
|
void *ret; |
||||
|
|
||||
|
if (size > vec->alc) |
||||
|
{ |
||||
|
size_t pagesize; |
||||
|
size_t alc; |
||||
|
void *base; |
||||
|
|
||||
|
pagesize = getpagesize (); |
||||
|
alc = vec->size + size; |
||||
|
if (vec->size == 0) |
||||
|
alc = 16 * size; |
||||
|
else if (alc < pagesize) |
||||
|
{ |
||||
|
alc *= 2; |
||||
|
if (alc > pagesize) |
||||
|
alc = pagesize; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
alc *= 2; |
||||
|
alc = (alc + pagesize - 1) & ~ (pagesize - 1); |
||||
|
} |
||||
|
base = backtrace_alloc (state, alc, error_callback, data); |
||||
|
if (base == NULL) |
||||
|
return NULL; |
||||
|
if (vec->base != NULL) |
||||
|
{ |
||||
|
memcpy (base, vec->base, vec->size); |
||||
|
backtrace_free (state, vec->base, vec->size + vec->alc, |
||||
|
error_callback, data); |
||||
|
} |
||||
|
vec->base = base; |
||||
|
vec->alc = alc - vec->size; |
||||
|
} |
||||
|
|
||||
|
ret = (char *) vec->base + vec->size; |
||||
|
vec->size += size; |
||||
|
vec->alc -= size; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/* Finish the current allocation on VEC. */ |
||||
|
|
||||
|
void * |
||||
|
backtrace_vector_finish ( |
||||
|
struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
struct backtrace_vector *vec, |
||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
||||
|
void *data ATTRIBUTE_UNUSED) |
||||
|
{ |
||||
|
void *ret; |
||||
|
|
||||
|
ret = vec->base; |
||||
|
vec->base = (char *) vec->base + vec->size; |
||||
|
vec->size = 0; |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/* Release any extra space allocated for VEC. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_vector_release (struct backtrace_state *state, |
||||
|
struct backtrace_vector *vec, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
size_t size; |
||||
|
size_t alc; |
||||
|
size_t aligned; |
||||
|
|
||||
|
/* Make sure that the block that we free is aligned on an 8-byte
|
||||
|
boundary. */ |
||||
|
size = vec->size; |
||||
|
alc = vec->alc; |
||||
|
aligned = (size + 7) & ~ (size_t) 7; |
||||
|
alc -= aligned - size; |
||||
|
|
||||
|
backtrace_free (state, (char *) vec->base + aligned, alc, |
||||
|
error_callback, data); |
||||
|
vec->alc = 0; |
||||
|
return 1; |
||||
|
} |
@ -0,0 +1,100 @@ |
|||||
|
/* mmapio.c -- File views using mmap.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/mman.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
#ifndef MAP_FAILED |
||||
|
#define MAP_FAILED ((void *)-1) |
||||
|
#endif |
||||
|
|
||||
|
/* This file implements file views and memory allocation when mmap is
|
||||
|
available. */ |
||||
|
|
||||
|
/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_get_view (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
int descriptor, off_t offset, size_t size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, struct backtrace_view *view) |
||||
|
{ |
||||
|
size_t pagesize; |
||||
|
unsigned int inpage; |
||||
|
off_t pageoff; |
||||
|
void *map; |
||||
|
|
||||
|
pagesize = getpagesize (); |
||||
|
inpage = offset % pagesize; |
||||
|
pageoff = offset - inpage; |
||||
|
|
||||
|
size += inpage; |
||||
|
size = (size + (pagesize - 1)) & ~ (pagesize - 1); |
||||
|
|
||||
|
map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff); |
||||
|
if (map == MAP_FAILED) |
||||
|
{ |
||||
|
error_callback (data, "mmap", errno); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
view->data = (char *) map + inpage; |
||||
|
view->base = map; |
||||
|
view->len = size; |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/* Release a view read by backtrace_get_view. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_release_view (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
struct backtrace_view *view, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
union { |
||||
|
const void *cv; |
||||
|
void *v; |
||||
|
} const_cast; |
||||
|
|
||||
|
const_cast.cv = view->base; |
||||
|
if (munmap (const_cast.v, view->len) < 0) |
||||
|
error_callback (data, "munmap", errno); |
||||
|
} |
@ -0,0 +1,83 @@ |
|||||
|
#!/bin/sh |
||||
|
# Like mv $1 $2, but if the files are the same, just delete $1. |
||||
|
# Status is zero if successful, nonzero otherwise. |
||||
|
|
||||
|
VERSION='2012-01-06 07:23'; # UTC |
||||
|
# The definition above must lie within the first 8 lines in order |
||||
|
# for the Emacs time-stamp write hook (at end) to update it. |
||||
|
# If you change this file with Emacs, please let the write hook |
||||
|
# do its job. Otherwise, update this string manually. |
||||
|
|
||||
|
# Copyright (C) 2002-2014 Free Software Foundation, Inc. |
||||
|
|
||||
|
# This program is free software: you can redistribute it and/or modify |
||||
|
# it under the terms of the GNU General Public License as published by |
||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||
|
# (at your option) any later version. |
||||
|
|
||||
|
# This program is distributed in the hope that it will be useful, |
||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
|
# GNU General Public License for more details. |
||||
|
|
||||
|
# You should have received a copy of the GNU General Public License |
||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||
|
|
||||
|
usage="usage: $0 SOURCE DEST" |
||||
|
|
||||
|
help="$usage |
||||
|
or: $0 OPTION |
||||
|
If SOURCE is different than DEST, then move it to DEST; else remove SOURCE. |
||||
|
|
||||
|
--help display this help and exit |
||||
|
--version output version information and exit |
||||
|
|
||||
|
The variable CMPPROG can be used to specify an alternative to 'cmp'. |
||||
|
|
||||
|
Report bugs to <bug-gnulib@gnu.org>." |
||||
|
|
||||
|
version=`expr "$VERSION" : '\([^ ]*\)'` |
||||
|
version="move-if-change (gnulib) $version |
||||
|
Copyright (C) 2011 Free Software Foundation, Inc. |
||||
|
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> |
||||
|
This is free software: you are free to change and redistribute it. |
||||
|
There is NO WARRANTY, to the extent permitted by law." |
||||
|
|
||||
|
cmpprog=${CMPPROG-cmp} |
||||
|
|
||||
|
for arg |
||||
|
do |
||||
|
case $arg in |
||||
|
--help | --hel | --he | --h) |
||||
|
exec echo "$help" ;; |
||||
|
--version | --versio | --versi | --vers | --ver | --ve | --v) |
||||
|
exec echo "$version" ;; |
||||
|
--) |
||||
|
shift |
||||
|
break ;; |
||||
|
-*) |
||||
|
echo "$0: invalid option: $arg" >&2 |
||||
|
exit 1 ;; |
||||
|
*) |
||||
|
break ;; |
||||
|
esac |
||||
|
done |
||||
|
|
||||
|
test $# -eq 2 || { echo "$0: $usage" >&2; exit 1; } |
||||
|
|
||||
|
if test -r "$2" && $cmpprog -- "$1" "$2" >/dev/null; then |
||||
|
rm -f -- "$1" |
||||
|
else |
||||
|
if mv -f -- "$1" "$2"; then :; else |
||||
|
# Ignore failure due to a concurrent move-if-change. |
||||
|
test -r "$2" && $cmpprog -- "$1" "$2" >/dev/null && rm -f -- "$1" |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
## Local Variables: |
||||
|
## eval: (add-hook 'write-file-hooks 'time-stamp) |
||||
|
## time-stamp-start: "VERSION='" |
||||
|
## time-stamp-format: "%:y-%02m-%02d %02H:%02M" |
||||
|
## time-stamp-time-zone: "UTC" |
||||
|
## time-stamp-end: "'; # UTC" |
||||
|
## End: |
@ -0,0 +1,66 @@ |
|||||
|
/* backtrace.c -- Entry point for stack backtrace library.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
|
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* This source file is compiled if the unwind library is not
|
||||
|
available. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_full (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
int skip ATTRIBUTE_UNUSED, |
||||
|
backtrace_full_callback callback ATTRIBUTE_UNUSED, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
error_callback (data, |
||||
|
"no stack trace because unwind library not available", |
||||
|
0); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
backtrace_simple (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
int skip ATTRIBUTE_UNUSED, |
||||
|
backtrace_simple_callback callback ATTRIBUTE_UNUSED, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
error_callback (data, |
||||
|
"no stack trace because unwind library not available", |
||||
|
0); |
||||
|
return 0; |
||||
|
} |
@ -0,0 +1,941 @@ |
|||||
|
/* pecoff.c -- Get debug data from a PE/COFFF file for backtraces.
|
||||
|
Copyright (C) 2015-2018 Free Software Foundation, Inc. |
||||
|
Adapted from elf.c by Tristan Gingold, AdaCore. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* Coff file header. */ |
||||
|
|
||||
|
typedef struct { |
||||
|
uint16_t machine; |
||||
|
uint16_t number_of_sections; |
||||
|
uint32_t time_date_stamp; |
||||
|
uint32_t pointer_to_symbol_table; |
||||
|
uint32_t number_of_symbols; |
||||
|
uint16_t size_of_optional_header; |
||||
|
uint16_t characteristics; |
||||
|
} b_coff_file_header; |
||||
|
|
||||
|
/* Coff optional header. */ |
||||
|
|
||||
|
typedef struct { |
||||
|
uint16_t magic; |
||||
|
uint8_t major_linker_version; |
||||
|
uint8_t minor_linker_version; |
||||
|
uint32_t size_of_code; |
||||
|
uint32_t size_of_initialized_data; |
||||
|
uint32_t size_of_uninitialized_data; |
||||
|
uint32_t address_of_entry_point; |
||||
|
uint32_t base_of_code; |
||||
|
union { |
||||
|
struct { |
||||
|
uint32_t base_of_data; |
||||
|
uint32_t image_base; |
||||
|
} pe; |
||||
|
struct { |
||||
|
uint64_t image_base; |
||||
|
} pep; |
||||
|
} u; |
||||
|
} b_coff_optional_header; |
||||
|
|
||||
|
/* Values of magic in optional header. */ |
||||
|
|
||||
|
#define PE_MAGIC 0x10b /* PE32 executable. */ |
||||
|
#define PEP_MAGIC 0x20b /* PE32+ executable (for 64bit targets). */ |
||||
|
|
||||
|
/* Coff section header. */ |
||||
|
|
||||
|
typedef struct { |
||||
|
char name[8]; |
||||
|
uint32_t virtual_size; |
||||
|
uint32_t virtual_address; |
||||
|
uint32_t size_of_raw_data; |
||||
|
uint32_t pointer_to_raw_data; |
||||
|
uint32_t pointer_to_relocations; |
||||
|
uint32_t pointer_to_line_numbers; |
||||
|
uint16_t number_of_relocations; |
||||
|
uint16_t number_of_line_numbers; |
||||
|
uint32_t characteristics; |
||||
|
} b_coff_section_header; |
||||
|
|
||||
|
/* Coff symbol name. */ |
||||
|
|
||||
|
typedef union { |
||||
|
char short_name[8]; |
||||
|
struct { |
||||
|
unsigned char zeroes[4]; |
||||
|
unsigned char off[4]; |
||||
|
} long_name; |
||||
|
} b_coff_name; |
||||
|
|
||||
|
/* Coff symbol (external representation which is unaligned). */ |
||||
|
|
||||
|
typedef struct { |
||||
|
b_coff_name name; |
||||
|
unsigned char value[4]; |
||||
|
unsigned char section_number[2]; |
||||
|
unsigned char type[2]; |
||||
|
unsigned char storage_class; |
||||
|
unsigned char number_of_aux_symbols; |
||||
|
} b_coff_external_symbol; |
||||
|
|
||||
|
/* Symbol types. */ |
||||
|
|
||||
|
#define N_TBSHFT 4 /* Shift for the derived type. */ |
||||
|
#define IMAGE_SYM_DTYPE_FUNCTION 2 /* Function derived type. */ |
||||
|
|
||||
|
/* Size of a coff symbol. */ |
||||
|
|
||||
|
#define SYM_SZ 18 |
||||
|
|
||||
|
/* Coff symbol, internal representation (aligned). */ |
||||
|
|
||||
|
typedef struct { |
||||
|
const char *name; |
||||
|
uint32_t value; |
||||
|
int16_t sec; |
||||
|
uint16_t type; |
||||
|
uint16_t sc; |
||||
|
} b_coff_internal_symbol; |
||||
|
|
||||
|
/* An index of sections we care about. */ |
||||
|
|
||||
|
enum debug_section |
||||
|
{ |
||||
|
DEBUG_INFO, |
||||
|
DEBUG_LINE, |
||||
|
DEBUG_ABBREV, |
||||
|
DEBUG_RANGES, |
||||
|
DEBUG_STR, |
||||
|
DEBUG_MAX |
||||
|
}; |
||||
|
|
||||
|
/* Names of sections, indexed by enum debug_section. */ |
||||
|
|
||||
|
static const char * const debug_section_names[DEBUG_MAX] = |
||||
|
{ |
||||
|
".debug_info", |
||||
|
".debug_line", |
||||
|
".debug_abbrev", |
||||
|
".debug_ranges", |
||||
|
".debug_str" |
||||
|
}; |
||||
|
|
||||
|
/* Information we gather for the sections we care about. */ |
||||
|
|
||||
|
struct debug_section_info |
||||
|
{ |
||||
|
/* Section file offset. */ |
||||
|
off_t offset; |
||||
|
/* Section size. */ |
||||
|
size_t size; |
||||
|
/* Section contents, after read from file. */ |
||||
|
const unsigned char *data; |
||||
|
}; |
||||
|
|
||||
|
/* Information we keep for an coff symbol. */ |
||||
|
|
||||
|
struct coff_symbol |
||||
|
{ |
||||
|
/* The name of the symbol. */ |
||||
|
const char *name; |
||||
|
/* The address of the symbol. */ |
||||
|
uintptr_t address; |
||||
|
}; |
||||
|
|
||||
|
/* Information to pass to coff_syminfo. */ |
||||
|
|
||||
|
struct coff_syminfo_data |
||||
|
{ |
||||
|
/* Symbols for the next module. */ |
||||
|
struct coff_syminfo_data *next; |
||||
|
/* The COFF symbols, sorted by address. */ |
||||
|
struct coff_symbol *symbols; |
||||
|
/* The number of symbols. */ |
||||
|
size_t count; |
||||
|
}; |
||||
|
|
||||
|
/* A dummy callback function used when we can't find any debug info. */ |
||||
|
|
||||
|
static int |
||||
|
coff_nodebug (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
uintptr_t pc ATTRIBUTE_UNUSED, |
||||
|
backtrace_full_callback callback ATTRIBUTE_UNUSED, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
error_callback (data, "no debug info in PE/COFF executable", -1); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* A dummy callback function used when we can't find a symbol
|
||||
|
table. */ |
||||
|
|
||||
|
static void |
||||
|
coff_nosyms (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
uintptr_t addr ATTRIBUTE_UNUSED, |
||||
|
backtrace_syminfo_callback callback ATTRIBUTE_UNUSED, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
error_callback (data, "no symbol table in PE/COFF executable", -1); |
||||
|
} |
||||
|
|
||||
|
/* Read a potentially unaligned 4 byte word at P, using native endianness. */ |
||||
|
|
||||
|
static uint32_t |
||||
|
coff_read4 (const unsigned char *p) |
||||
|
{ |
||||
|
uint32_t res; |
||||
|
|
||||
|
memcpy (&res, p, 4); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
/* Read a potentially unaligned 2 byte word at P, using native endianness.
|
||||
|
All 2 byte word in symbols are always aligned, but for coherency all |
||||
|
fields are declared as char arrays. */ |
||||
|
|
||||
|
static uint16_t |
||||
|
coff_read2 (const unsigned char *p) |
||||
|
{ |
||||
|
uint16_t res; |
||||
|
|
||||
|
memcpy (&res, p, sizeof (res)); |
||||
|
return res; |
||||
|
} |
||||
|
|
||||
|
/* Return the length (without the trailing 0) of a COFF short name. */ |
||||
|
|
||||
|
static size_t |
||||
|
coff_short_name_len (const char *name) |
||||
|
{ |
||||
|
int i; |
||||
|
|
||||
|
for (i = 0; i < 8; i++) |
||||
|
if (name[i] == 0) |
||||
|
return i; |
||||
|
return 8; |
||||
|
} |
||||
|
|
||||
|
/* Return true iff COFF short name CNAME is the same as NAME (a NUL-terminated
|
||||
|
string). */ |
||||
|
|
||||
|
static int |
||||
|
coff_short_name_eq (const char *name, const char *cname) |
||||
|
{ |
||||
|
int i; |
||||
|
|
||||
|
for (i = 0; i < 8; i++) |
||||
|
{ |
||||
|
if (name[i] != cname[i]) |
||||
|
return 0; |
||||
|
if (name[i] == 0) |
||||
|
return 1; |
||||
|
} |
||||
|
return name[8] == 0; |
||||
|
} |
||||
|
|
||||
|
/* Return true iff NAME is the same as string at offset OFF. */ |
||||
|
|
||||
|
static int |
||||
|
coff_long_name_eq (const char *name, unsigned int off, |
||||
|
struct backtrace_view *str_view) |
||||
|
{ |
||||
|
if (off >= str_view->len) |
||||
|
return 0; |
||||
|
return strcmp (name, (const char *)str_view->data + off) == 0; |
||||
|
} |
||||
|
|
||||
|
/* Compare struct coff_symbol for qsort. */ |
||||
|
|
||||
|
static int |
||||
|
coff_symbol_compare (const void *v1, const void *v2) |
||||
|
{ |
||||
|
const struct coff_symbol *e1 = (const struct coff_symbol *) v1; |
||||
|
const struct coff_symbol *e2 = (const struct coff_symbol *) v2; |
||||
|
|
||||
|
if (e1->address < e2->address) |
||||
|
return -1; |
||||
|
else if (e1->address > e2->address) |
||||
|
return 1; |
||||
|
else |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* Convert SYM to internal (and aligned) format ISYM, using string table
|
||||
|
from STRTAB and STRTAB_SIZE, and number of sections SECTS_NUM. |
||||
|
Return -1 in case of error (invalid section number or string index). */ |
||||
|
|
||||
|
static int |
||||
|
coff_expand_symbol (b_coff_internal_symbol *isym, |
||||
|
const b_coff_external_symbol *sym, |
||||
|
uint16_t sects_num, |
||||
|
const unsigned char *strtab, size_t strtab_size) |
||||
|
{ |
||||
|
isym->type = coff_read2 (sym->type); |
||||
|
isym->sec = coff_read2 (sym->section_number); |
||||
|
isym->sc = sym->storage_class; |
||||
|
|
||||
|
if (isym->sec > 0 && (uint16_t) isym->sec > sects_num) |
||||
|
return -1; |
||||
|
if (sym->name.short_name[0] != 0) |
||||
|
isym->name = sym->name.short_name; |
||||
|
else |
||||
|
{ |
||||
|
uint32_t off = coff_read4 (sym->name.long_name.off); |
||||
|
|
||||
|
if (off >= strtab_size) |
||||
|
return -1; |
||||
|
isym->name = (const char *) strtab + off; |
||||
|
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* Return true iff SYM is a defined symbol for a function. Data symbols
|
||||
|
aren't considered because they aren't easily identified (same type as |
||||
|
section names, presence of symbols defined by the linker script). */ |
||||
|
|
||||
|
static int |
||||
|
coff_is_function_symbol (const b_coff_internal_symbol *isym) |
||||
|
{ |
||||
|
return (isym->type >> N_TBSHFT) == IMAGE_SYM_DTYPE_FUNCTION |
||||
|
&& isym->sec > 0; |
||||
|
} |
||||
|
|
||||
|
/* Initialize the symbol table info for coff_syminfo. */ |
||||
|
|
||||
|
static int |
||||
|
coff_initialize_syminfo (struct backtrace_state *state, |
||||
|
uintptr_t base_address, |
||||
|
const b_coff_section_header *sects, size_t sects_num, |
||||
|
const b_coff_external_symbol *syms, size_t syms_size, |
||||
|
const unsigned char *strtab, size_t strtab_size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, struct coff_syminfo_data *sdata) |
||||
|
{ |
||||
|
size_t syms_count; |
||||
|
char *coff_symstr; |
||||
|
size_t coff_symstr_len; |
||||
|
size_t coff_symbol_count; |
||||
|
size_t coff_symbol_size; |
||||
|
struct coff_symbol *coff_symbols; |
||||
|
struct coff_symbol *coff_sym; |
||||
|
char *coff_str; |
||||
|
size_t i; |
||||
|
|
||||
|
syms_count = syms_size / SYM_SZ; |
||||
|
|
||||
|
/* We only care about function symbols. Count them. Also count size of
|
||||
|
strings for in-symbol names. */ |
||||
|
coff_symbol_count = 0; |
||||
|
coff_symstr_len = 0; |
||||
|
for (i = 0; i < syms_count; ++i) |
||||
|
{ |
||||
|
const b_coff_external_symbol *asym = &syms[i]; |
||||
|
b_coff_internal_symbol isym; |
||||
|
|
||||
|
if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size) < 0) |
||||
|
{ |
||||
|
error_callback (data, "invalid section or offset in coff symbol", 0); |
||||
|
return 0; |
||||
|
} |
||||
|
if (coff_is_function_symbol (&isym)) |
||||
|
{ |
||||
|
++coff_symbol_count; |
||||
|
if (asym->name.short_name[0] != 0) |
||||
|
coff_symstr_len += coff_short_name_len (asym->name.short_name) + 1; |
||||
|
} |
||||
|
|
||||
|
i += asym->number_of_aux_symbols; |
||||
|
} |
||||
|
|
||||
|
coff_symbol_size = (coff_symbol_count + 1) * sizeof (struct coff_symbol); |
||||
|
coff_symbols = ((struct coff_symbol *) |
||||
|
backtrace_alloc (state, coff_symbol_size, error_callback, |
||||
|
data)); |
||||
|
if (coff_symbols == NULL) |
||||
|
return 0; |
||||
|
|
||||
|
/* Allocate memory for symbols strings. */ |
||||
|
if (coff_symstr_len > 0) |
||||
|
{ |
||||
|
coff_symstr = ((char *) |
||||
|
backtrace_alloc (state, coff_symstr_len, error_callback, |
||||
|
data)); |
||||
|
if (coff_symstr == NULL) |
||||
|
{ |
||||
|
backtrace_free (state, coff_symbols, coff_symbol_size, |
||||
|
error_callback, data); |
||||
|
return 0; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
coff_symstr = NULL; |
||||
|
|
||||
|
/* Copy symbols. */ |
||||
|
coff_sym = coff_symbols; |
||||
|
coff_str = coff_symstr; |
||||
|
for (i = 0; i < syms_count; ++i) |
||||
|
{ |
||||
|
const b_coff_external_symbol *asym = &syms[i]; |
||||
|
b_coff_internal_symbol isym; |
||||
|
|
||||
|
if (coff_expand_symbol (&isym, asym, sects_num, strtab, strtab_size)) |
||||
|
{ |
||||
|
/* Should not fail, as it was already tested in the previous
|
||||
|
loop. */ |
||||
|
abort (); |
||||
|
} |
||||
|
if (coff_is_function_symbol (&isym)) |
||||
|
{ |
||||
|
const char *name; |
||||
|
int16_t secnum; |
||||
|
|
||||
|
if (asym->name.short_name[0] != 0) |
||||
|
{ |
||||
|
size_t len = coff_short_name_len (isym.name); |
||||
|
name = coff_str; |
||||
|
memcpy (coff_str, isym.name, len); |
||||
|
coff_str[len] = 0; |
||||
|
coff_str += len + 1; |
||||
|
} |
||||
|
else |
||||
|
name = isym.name; |
||||
|
|
||||
|
/* Strip leading '_'. */ |
||||
|
if (name[0] == '_') |
||||
|
name++; |
||||
|
|
||||
|
/* Symbol value is section relative, so we need to read the address
|
||||
|
of its section. */ |
||||
|
secnum = coff_read2 (asym->section_number); |
||||
|
|
||||
|
coff_sym->name = name; |
||||
|
coff_sym->address = (coff_read4 (asym->value) |
||||
|
+ sects[secnum - 1].virtual_address |
||||
|
+ base_address); |
||||
|
coff_sym++; |
||||
|
} |
||||
|
|
||||
|
i += asym->number_of_aux_symbols; |
||||
|
} |
||||
|
|
||||
|
/* End of symbols marker. */ |
||||
|
coff_sym->name = NULL; |
||||
|
coff_sym->address = -1; |
||||
|
|
||||
|
backtrace_qsort (coff_symbols, coff_symbol_count, |
||||
|
sizeof (struct coff_symbol), coff_symbol_compare); |
||||
|
|
||||
|
sdata->next = NULL; |
||||
|
sdata->symbols = coff_symbols; |
||||
|
sdata->count = coff_symbol_count; |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/* Add EDATA to the list in STATE. */ |
||||
|
|
||||
|
static void |
||||
|
coff_add_syminfo_data (struct backtrace_state *state, |
||||
|
struct coff_syminfo_data *sdata) |
||||
|
{ |
||||
|
if (!state->threaded) |
||||
|
{ |
||||
|
struct coff_syminfo_data **pp; |
||||
|
|
||||
|
for (pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data; |
||||
|
*pp != NULL; |
||||
|
pp = &(*pp)->next) |
||||
|
; |
||||
|
*pp = sdata; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
while (1) |
||||
|
{ |
||||
|
struct coff_syminfo_data **pp; |
||||
|
|
||||
|
pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data; |
||||
|
|
||||
|
while (1) |
||||
|
{ |
||||
|
struct coff_syminfo_data *p; |
||||
|
|
||||
|
p = backtrace_atomic_load_pointer (pp); |
||||
|
|
||||
|
if (p == NULL) |
||||
|
break; |
||||
|
|
||||
|
pp = &p->next; |
||||
|
} |
||||
|
|
||||
|
if (__sync_bool_compare_and_swap (pp, NULL, sdata)) |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* Compare an ADDR against an elf_symbol for bsearch. We allocate one
|
||||
|
extra entry in the array so that this can look safely at the next |
||||
|
entry. */ |
||||
|
|
||||
|
static int |
||||
|
coff_symbol_search (const void *vkey, const void *ventry) |
||||
|
{ |
||||
|
const uintptr_t *key = (const uintptr_t *) vkey; |
||||
|
const struct coff_symbol *entry = (const struct coff_symbol *) ventry; |
||||
|
uintptr_t addr; |
||||
|
|
||||
|
addr = *key; |
||||
|
if (addr < entry->address) |
||||
|
return -1; |
||||
|
else if (addr >= entry[1].address) |
||||
|
return 1; |
||||
|
else |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* Return the symbol name and value for an ADDR. */ |
||||
|
|
||||
|
static void |
||||
|
coff_syminfo (struct backtrace_state *state, uintptr_t addr, |
||||
|
backtrace_syminfo_callback callback, |
||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
||||
|
void *data) |
||||
|
{ |
||||
|
struct coff_syminfo_data *sdata; |
||||
|
struct coff_symbol *sym = NULL; |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
{ |
||||
|
for (sdata = (struct coff_syminfo_data *) state->syminfo_data; |
||||
|
sdata != NULL; |
||||
|
sdata = sdata->next) |
||||
|
{ |
||||
|
sym = ((struct coff_symbol *) |
||||
|
bsearch (&addr, sdata->symbols, sdata->count, |
||||
|
sizeof (struct coff_symbol), coff_symbol_search)); |
||||
|
if (sym != NULL) |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
struct coff_syminfo_data **pp; |
||||
|
|
||||
|
pp = (struct coff_syminfo_data **) (void *) &state->syminfo_data; |
||||
|
while (1) |
||||
|
{ |
||||
|
sdata = backtrace_atomic_load_pointer (pp); |
||||
|
if (sdata == NULL) |
||||
|
break; |
||||
|
|
||||
|
sym = ((struct coff_symbol *) |
||||
|
bsearch (&addr, sdata->symbols, sdata->count, |
||||
|
sizeof (struct coff_symbol), coff_symbol_search)); |
||||
|
if (sym != NULL) |
||||
|
break; |
||||
|
|
||||
|
pp = &sdata->next; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (sym == NULL) |
||||
|
callback (data, addr, NULL, 0, 0); |
||||
|
else |
||||
|
callback (data, addr, sym->name, sym->address, 0); |
||||
|
} |
||||
|
|
||||
|
/* Add the backtrace data for one PE/COFF file. Returns 1 on success,
|
||||
|
0 on failure (in both cases descriptor is closed). */ |
||||
|
|
||||
|
static int |
||||
|
coff_add (struct backtrace_state *state, int descriptor, |
||||
|
backtrace_error_callback error_callback, void *data, |
||||
|
fileline *fileline_fn, int *found_sym, int *found_dwarf) |
||||
|
{ |
||||
|
struct backtrace_view fhdr_view; |
||||
|
off_t fhdr_off; |
||||
|
int magic_ok; |
||||
|
b_coff_file_header fhdr; |
||||
|
off_t opt_sects_off; |
||||
|
size_t opt_sects_size; |
||||
|
unsigned int sects_num; |
||||
|
struct backtrace_view sects_view; |
||||
|
int sects_view_valid; |
||||
|
const b_coff_optional_header *opt_hdr; |
||||
|
const b_coff_section_header *sects; |
||||
|
struct backtrace_view str_view; |
||||
|
int str_view_valid; |
||||
|
size_t str_size; |
||||
|
off_t str_off; |
||||
|
struct backtrace_view syms_view; |
||||
|
off_t syms_off; |
||||
|
size_t syms_size; |
||||
|
int syms_view_valid; |
||||
|
unsigned int syms_num; |
||||
|
unsigned int i; |
||||
|
struct debug_section_info sections[DEBUG_MAX]; |
||||
|
off_t min_offset; |
||||
|
off_t max_offset; |
||||
|
struct backtrace_view debug_view; |
||||
|
int debug_view_valid; |
||||
|
uintptr_t image_base; |
||||
|
|
||||
|
*found_sym = 0; |
||||
|
*found_dwarf = 0; |
||||
|
|
||||
|
sects_view_valid = 0; |
||||
|
syms_view_valid = 0; |
||||
|
str_view_valid = 0; |
||||
|
debug_view_valid = 0; |
||||
|
|
||||
|
/* Map the MS-DOS stub (if any) and extract file header offset. */ |
||||
|
if (!backtrace_get_view (state, descriptor, 0, 0x40, error_callback, |
||||
|
data, &fhdr_view)) |
||||
|
goto fail; |
||||
|
|
||||
|
{ |
||||
|
const unsigned char *vptr = fhdr_view.data; |
||||
|
|
||||
|
if (vptr[0] == 'M' && vptr[1] == 'Z') |
||||
|
fhdr_off = coff_read4 (vptr + 0x3c); |
||||
|
else |
||||
|
fhdr_off = 0; |
||||
|
} |
||||
|
|
||||
|
backtrace_release_view (state, &fhdr_view, error_callback, data); |
||||
|
|
||||
|
/* Map the coff file header. */ |
||||
|
if (!backtrace_get_view (state, descriptor, fhdr_off, |
||||
|
sizeof (b_coff_file_header) + 4, |
||||
|
error_callback, data, &fhdr_view)) |
||||
|
goto fail; |
||||
|
|
||||
|
if (fhdr_off != 0) |
||||
|
{ |
||||
|
const char *magic = (const char *) fhdr_view.data; |
||||
|
magic_ok = memcmp (magic, "PE\0", 4) == 0; |
||||
|
fhdr_off += 4; |
||||
|
|
||||
|
memcpy (&fhdr, fhdr_view.data + 4, sizeof fhdr); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
memcpy (&fhdr, fhdr_view.data, sizeof fhdr); |
||||
|
/* TODO: test fhdr.machine for coff but non-PE platforms. */ |
||||
|
magic_ok = 0; |
||||
|
} |
||||
|
backtrace_release_view (state, &fhdr_view, error_callback, data); |
||||
|
|
||||
|
if (!magic_ok) |
||||
|
{ |
||||
|
error_callback (data, "executable file is not COFF", 0); |
||||
|
goto fail; |
||||
|
} |
||||
|
|
||||
|
sects_num = fhdr.number_of_sections; |
||||
|
syms_num = fhdr.number_of_symbols; |
||||
|
|
||||
|
opt_sects_off = fhdr_off + sizeof (fhdr); |
||||
|
opt_sects_size = (fhdr.size_of_optional_header |
||||
|
+ sects_num * sizeof (b_coff_section_header)); |
||||
|
|
||||
|
/* To translate PC to file/line when using DWARF, we need to find
|
||||
|
the .debug_info and .debug_line sections. */ |
||||
|
|
||||
|
/* Read the optional header and the section headers. */ |
||||
|
|
||||
|
if (!backtrace_get_view (state, descriptor, opt_sects_off, opt_sects_size, |
||||
|
error_callback, data, §s_view)) |
||||
|
goto fail; |
||||
|
sects_view_valid = 1; |
||||
|
opt_hdr = (const b_coff_optional_header *) sects_view.data; |
||||
|
sects = (const b_coff_section_header *) |
||||
|
(sects_view.data + fhdr.size_of_optional_header); |
||||
|
|
||||
|
if (fhdr.size_of_optional_header > sizeof (*opt_hdr)) |
||||
|
{ |
||||
|
if (opt_hdr->magic == PE_MAGIC) |
||||
|
image_base = opt_hdr->u.pe.image_base; |
||||
|
else if (opt_hdr->magic == PEP_MAGIC) |
||||
|
image_base = opt_hdr->u.pep.image_base; |
||||
|
else |
||||
|
{ |
||||
|
error_callback (data, "bad magic in PE optional header", 0); |
||||
|
goto fail; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
image_base = 0; |
||||
|
|
||||
|
/* Read the symbol table and the string table. */ |
||||
|
|
||||
|
if (fhdr.pointer_to_symbol_table == 0) |
||||
|
{ |
||||
|
/* No symbol table, no string table. */ |
||||
|
str_off = 0; |
||||
|
str_size = 0; |
||||
|
syms_num = 0; |
||||
|
syms_size = 0; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
/* Symbol table is followed by the string table. The string table
|
||||
|
starts with its length (on 4 bytes). |
||||
|
Map the symbol table and the length of the string table. */ |
||||
|
syms_off = fhdr.pointer_to_symbol_table; |
||||
|
syms_size = syms_num * SYM_SZ; |
||||
|
|
||||
|
if (!backtrace_get_view (state, descriptor, syms_off, syms_size + 4, |
||||
|
error_callback, data, &syms_view)) |
||||
|
goto fail; |
||||
|
syms_view_valid = 1; |
||||
|
|
||||
|
str_size = coff_read4 (syms_view.data + syms_size); |
||||
|
|
||||
|
str_off = syms_off + syms_size; |
||||
|
|
||||
|
if (str_size > 4) |
||||
|
{ |
||||
|
/* Map string table (including the length word). */ |
||||
|
|
||||
|
if (!backtrace_get_view (state, descriptor, str_off, str_size, |
||||
|
error_callback, data, &str_view)) |
||||
|
goto fail; |
||||
|
str_view_valid = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
memset (sections, 0, sizeof sections); |
||||
|
|
||||
|
/* Look for the symbol table. */ |
||||
|
for (i = 0; i < sects_num; ++i) |
||||
|
{ |
||||
|
const b_coff_section_header *s = sects + i; |
||||
|
unsigned int str_off; |
||||
|
int j; |
||||
|
|
||||
|
if (s->name[0] == '/') |
||||
|
{ |
||||
|
/* Extended section name. */ |
||||
|
str_off = atoi (s->name + 1); |
||||
|
} |
||||
|
else |
||||
|
str_off = 0; |
||||
|
|
||||
|
for (j = 0; j < (int) DEBUG_MAX; ++j) |
||||
|
{ |
||||
|
const char *dbg_name = debug_section_names[j]; |
||||
|
int match; |
||||
|
|
||||
|
if (str_off != 0) |
||||
|
match = coff_long_name_eq (dbg_name, str_off, &str_view); |
||||
|
else |
||||
|
match = coff_short_name_eq (dbg_name, s->name); |
||||
|
if (match) |
||||
|
{ |
||||
|
sections[j].offset = s->pointer_to_raw_data; |
||||
|
sections[j].size = s->virtual_size <= s->size_of_raw_data ? |
||||
|
s->virtual_size : s->size_of_raw_data; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (syms_num != 0) |
||||
|
{ |
||||
|
struct coff_syminfo_data *sdata; |
||||
|
|
||||
|
sdata = ((struct coff_syminfo_data *) |
||||
|
backtrace_alloc (state, sizeof *sdata, error_callback, data)); |
||||
|
if (sdata == NULL) |
||||
|
goto fail; |
||||
|
|
||||
|
if (!coff_initialize_syminfo (state, image_base, |
||||
|
sects, sects_num, |
||||
|
syms_view.data, syms_size, |
||||
|
str_view.data, str_size, |
||||
|
error_callback, data, sdata)) |
||||
|
{ |
||||
|
backtrace_free (state, sdata, sizeof *sdata, error_callback, data); |
||||
|
goto fail; |
||||
|
} |
||||
|
|
||||
|
*found_sym = 1; |
||||
|
|
||||
|
coff_add_syminfo_data (state, sdata); |
||||
|
} |
||||
|
|
||||
|
backtrace_release_view (state, §s_view, error_callback, data); |
||||
|
sects_view_valid = 0; |
||||
|
if (syms_view_valid) |
||||
|
{ |
||||
|
backtrace_release_view (state, &syms_view, error_callback, data); |
||||
|
syms_view_valid = 0; |
||||
|
} |
||||
|
|
||||
|
/* Read all the debug sections in a single view, since they are
|
||||
|
probably adjacent in the file. We never release this view. */ |
||||
|
|
||||
|
min_offset = 0; |
||||
|
max_offset = 0; |
||||
|
for (i = 0; i < (int) DEBUG_MAX; ++i) |
||||
|
{ |
||||
|
off_t end; |
||||
|
|
||||
|
if (sections[i].size == 0) |
||||
|
continue; |
||||
|
if (min_offset == 0 || sections[i].offset < min_offset) |
||||
|
min_offset = sections[i].offset; |
||||
|
end = sections[i].offset + sections[i].size; |
||||
|
if (end > max_offset) |
||||
|
max_offset = end; |
||||
|
} |
||||
|
if (min_offset == 0 || max_offset == 0) |
||||
|
{ |
||||
|
if (!backtrace_close (descriptor, error_callback, data)) |
||||
|
goto fail; |
||||
|
*fileline_fn = coff_nodebug; |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
if (!backtrace_get_view (state, descriptor, min_offset, |
||||
|
max_offset - min_offset, |
||||
|
error_callback, data, &debug_view)) |
||||
|
goto fail; |
||||
|
debug_view_valid = 1; |
||||
|
|
||||
|
/* We've read all we need from the executable. */ |
||||
|
if (!backtrace_close (descriptor, error_callback, data)) |
||||
|
goto fail; |
||||
|
descriptor = -1; |
||||
|
|
||||
|
for (i = 0; i < (int) DEBUG_MAX; ++i) |
||||
|
{ |
||||
|
if (sections[i].size == 0) |
||||
|
sections[i].data = NULL; |
||||
|
else |
||||
|
sections[i].data = ((const unsigned char *) debug_view.data |
||||
|
+ (sections[i].offset - min_offset)); |
||||
|
} |
||||
|
|
||||
|
if (!backtrace_dwarf_add (state, /* base_address */ 0, |
||||
|
sections[DEBUG_INFO].data, |
||||
|
sections[DEBUG_INFO].size, |
||||
|
sections[DEBUG_LINE].data, |
||||
|
sections[DEBUG_LINE].size, |
||||
|
sections[DEBUG_ABBREV].data, |
||||
|
sections[DEBUG_ABBREV].size, |
||||
|
sections[DEBUG_RANGES].data, |
||||
|
sections[DEBUG_RANGES].size, |
||||
|
sections[DEBUG_STR].data, |
||||
|
sections[DEBUG_STR].size, |
||||
|
0, /* FIXME */ |
||||
|
error_callback, data, fileline_fn)) |
||||
|
goto fail; |
||||
|
|
||||
|
*found_dwarf = 1; |
||||
|
|
||||
|
return 1; |
||||
|
|
||||
|
fail: |
||||
|
if (sects_view_valid) |
||||
|
backtrace_release_view (state, §s_view, error_callback, data); |
||||
|
if (str_view_valid) |
||||
|
backtrace_release_view (state, &str_view, error_callback, data); |
||||
|
if (syms_view_valid) |
||||
|
backtrace_release_view (state, &syms_view, error_callback, data); |
||||
|
if (debug_view_valid) |
||||
|
backtrace_release_view (state, &debug_view, error_callback, data); |
||||
|
if (descriptor != -1) |
||||
|
backtrace_close (descriptor, error_callback, data); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* Initialize the backtrace data we need from an ELF executable. At
|
||||
|
the ELF level, all we need to do is find the debug info |
||||
|
sections. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_initialize (struct backtrace_state *state, |
||||
|
const char *filename ATTRIBUTE_UNUSED, int descriptor, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, fileline *fileline_fn) |
||||
|
{ |
||||
|
int ret; |
||||
|
int found_sym; |
||||
|
int found_dwarf; |
||||
|
fileline coff_fileline_fn; |
||||
|
|
||||
|
ret = coff_add (state, descriptor, error_callback, data, |
||||
|
&coff_fileline_fn, &found_sym, &found_dwarf); |
||||
|
if (!ret) |
||||
|
return 0; |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
{ |
||||
|
if (found_sym) |
||||
|
state->syminfo_fn = coff_syminfo; |
||||
|
else if (state->syminfo_fn == NULL) |
||||
|
state->syminfo_fn = coff_nosyms; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (found_sym) |
||||
|
backtrace_atomic_store_pointer (&state->syminfo_fn, coff_syminfo); |
||||
|
else |
||||
|
__sync_bool_compare_and_swap (&state->syminfo_fn, NULL, coff_nosyms); |
||||
|
} |
||||
|
|
||||
|
if (!state->threaded) |
||||
|
{ |
||||
|
if (state->fileline_fn == NULL || state->fileline_fn == coff_nodebug) |
||||
|
*fileline_fn = coff_fileline_fn; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
fileline current_fn; |
||||
|
|
||||
|
current_fn = backtrace_atomic_load_pointer (&state->fileline_fn); |
||||
|
if (current_fn == NULL || current_fn == coff_nodebug) |
||||
|
*fileline_fn = coff_fileline_fn; |
||||
|
} |
||||
|
|
||||
|
return 1; |
||||
|
} |
@ -0,0 +1,100 @@ |
|||||
|
/* posix.c -- POSIX file I/O routines for the backtrace library.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/stat.h> |
||||
|
#include <fcntl.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
#ifndef O_BINARY |
||||
|
#define O_BINARY 0 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef O_CLOEXEC |
||||
|
#define O_CLOEXEC 0 |
||||
|
#endif |
||||
|
|
||||
|
#ifndef FD_CLOEXEC |
||||
|
#define FD_CLOEXEC 1 |
||||
|
#endif |
||||
|
|
||||
|
/* Open a file for reading. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_open (const char *filename, backtrace_error_callback error_callback, |
||||
|
void *data, int *does_not_exist) |
||||
|
{ |
||||
|
int descriptor; |
||||
|
|
||||
|
if (does_not_exist != NULL) |
||||
|
*does_not_exist = 0; |
||||
|
|
||||
|
descriptor = open (filename, (int) (O_RDONLY | O_BINARY | O_CLOEXEC)); |
||||
|
if (descriptor < 0) |
||||
|
{ |
||||
|
if (does_not_exist != NULL && errno == ENOENT) |
||||
|
*does_not_exist = 1; |
||||
|
else |
||||
|
error_callback (data, filename, errno); |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
#ifdef HAVE_FCNTL |
||||
|
/* Set FD_CLOEXEC just in case the kernel does not support
|
||||
|
O_CLOEXEC. It doesn't matter if this fails for some reason. |
||||
|
FIXME: At some point it should be safe to only do this if |
||||
|
O_CLOEXEC == 0. */ |
||||
|
fcntl (descriptor, F_SETFD, FD_CLOEXEC); |
||||
|
#endif |
||||
|
|
||||
|
return descriptor; |
||||
|
} |
||||
|
|
||||
|
/* Close DESCRIPTOR. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_close (int descriptor, backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
if (close (descriptor) < 0) |
||||
|
{ |
||||
|
error_callback (data, "close", errno); |
||||
|
return 0; |
||||
|
} |
||||
|
return 1; |
||||
|
} |
@ -0,0 +1,92 @@ |
|||||
|
/* print.c -- Print the current backtrace.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <string.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* Passed to callbacks. */ |
||||
|
|
||||
|
struct print_data |
||||
|
{ |
||||
|
struct backtrace_state *state; |
||||
|
FILE *f; |
||||
|
}; |
||||
|
|
||||
|
/* Print one level of a backtrace. */ |
||||
|
|
||||
|
static int |
||||
|
print_callback (void *data, uintptr_t pc, const char *filename, int lineno, |
||||
|
const char *function) |
||||
|
{ |
||||
|
struct print_data *pdata = (struct print_data *) data; |
||||
|
|
||||
|
fprintf (pdata->f, "0x%lx %s\n\t%s:%d\n", |
||||
|
(unsigned long) pc, |
||||
|
function == NULL ? "???" : function, |
||||
|
filename == NULL ? "???" : filename, |
||||
|
lineno); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* Print errors to stderr. */ |
||||
|
|
||||
|
static void |
||||
|
error_callback (void *data, const char *msg, int errnum) |
||||
|
{ |
||||
|
struct print_data *pdata = (struct print_data *) data; |
||||
|
|
||||
|
if (pdata->state->filename != NULL) |
||||
|
fprintf (stderr, "%s: ", pdata->state->filename); |
||||
|
fprintf (stderr, "libbacktrace: %s", msg); |
||||
|
if (errnum > 0) |
||||
|
fprintf (stderr, ": %s", strerror (errnum)); |
||||
|
fputc ('\n', stderr); |
||||
|
} |
||||
|
|
||||
|
/* Print a backtrace. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_print (struct backtrace_state *state, int skip, FILE *f) |
||||
|
{ |
||||
|
struct print_data data; |
||||
|
|
||||
|
data.state = state; |
||||
|
data.f = f; |
||||
|
backtrace_full (state, skip + 1, print_callback, error_callback, |
||||
|
(void *) &data); |
||||
|
} |
@ -0,0 +1,96 @@ |
|||||
|
/* read.c -- File views without mmap.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <unistd.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* This file implements file views when mmap is not available. */ |
||||
|
|
||||
|
/* Create a view of SIZE bytes from DESCRIPTOR at OFFSET. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_get_view (struct backtrace_state *state, int descriptor, |
||||
|
off_t offset, size_t size, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data, struct backtrace_view *view) |
||||
|
{ |
||||
|
ssize_t got; |
||||
|
|
||||
|
if (lseek (descriptor, offset, SEEK_SET) < 0) |
||||
|
{ |
||||
|
error_callback (data, "lseek", errno); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
view->base = backtrace_alloc (state, size, error_callback, data); |
||||
|
if (view->base == NULL) |
||||
|
return 0; |
||||
|
view->data = view->base; |
||||
|
view->len = size; |
||||
|
|
||||
|
got = read (descriptor, view->base, size); |
||||
|
if (got < 0) |
||||
|
{ |
||||
|
error_callback (data, "read", errno); |
||||
|
free (view->base); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
if ((size_t) got < size) |
||||
|
{ |
||||
|
error_callback (data, "file too short", 0); |
||||
|
free (view->base); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
/* Release a view read by backtrace_get_view. */ |
||||
|
|
||||
|
void |
||||
|
backtrace_release_view (struct backtrace_state *state, |
||||
|
struct backtrace_view *view, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
backtrace_free (state, view->base, view->len, error_callback, data); |
||||
|
view->data = NULL; |
||||
|
view->base = NULL; |
||||
|
} |
@ -0,0 +1,108 @@ |
|||||
|
/* simple.c -- The backtrace_simple function.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include "unwind.h" |
||||
|
#include "backtrace.h" |
||||
|
|
||||
|
/* The simple_backtrace routine. */ |
||||
|
|
||||
|
/* Data passed through _Unwind_Backtrace. */ |
||||
|
|
||||
|
struct backtrace_simple_data |
||||
|
{ |
||||
|
/* Number of frames to skip. */ |
||||
|
int skip; |
||||
|
/* Library state. */ |
||||
|
struct backtrace_state *state; |
||||
|
/* Callback routine. */ |
||||
|
backtrace_simple_callback callback; |
||||
|
/* Error callback routine. */ |
||||
|
backtrace_error_callback error_callback; |
||||
|
/* Data to pass to callback routine. */ |
||||
|
void *data; |
||||
|
/* Value to return from backtrace. */ |
||||
|
int ret; |
||||
|
}; |
||||
|
|
||||
|
/* Unwind library callback routine. This is passd to
|
||||
|
_Unwind_Backtrace. */ |
||||
|
|
||||
|
static _Unwind_Reason_Code |
||||
|
simple_unwind (struct _Unwind_Context *context, void *vdata) |
||||
|
{ |
||||
|
struct backtrace_simple_data *bdata = (struct backtrace_simple_data *) vdata; |
||||
|
uintptr_t pc; |
||||
|
int ip_before_insn = 0; |
||||
|
|
||||
|
#ifdef HAVE_GETIPINFO |
||||
|
pc = _Unwind_GetIPInfo (context, &ip_before_insn); |
||||
|
#else |
||||
|
pc = _Unwind_GetIP (context); |
||||
|
#endif |
||||
|
|
||||
|
if (bdata->skip > 0) |
||||
|
{ |
||||
|
--bdata->skip; |
||||
|
return _URC_NO_REASON; |
||||
|
} |
||||
|
|
||||
|
if (!ip_before_insn) |
||||
|
--pc; |
||||
|
|
||||
|
bdata->ret = bdata->callback (bdata->data, pc); |
||||
|
|
||||
|
if (bdata->ret != 0) |
||||
|
return _URC_END_OF_STACK; |
||||
|
|
||||
|
return _URC_NO_REASON; |
||||
|
} |
||||
|
|
||||
|
/* Get a simple stack backtrace. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_simple (struct backtrace_state *state, int skip, |
||||
|
backtrace_simple_callback callback, |
||||
|
backtrace_error_callback error_callback, void *data) |
||||
|
{ |
||||
|
struct backtrace_simple_data bdata; |
||||
|
|
||||
|
bdata.skip = skip + 1; |
||||
|
bdata.state = state; |
||||
|
bdata.callback = callback; |
||||
|
bdata.error_callback = error_callback; |
||||
|
bdata.data = data; |
||||
|
bdata.ret = 0; |
||||
|
_Unwind_Backtrace (simple_unwind, &bdata); |
||||
|
return bdata.ret; |
||||
|
} |
@ -0,0 +1,108 @@ |
|||||
|
/* sort.c -- Sort without allocating memory
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <stddef.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* The GNU glibc version of qsort allocates memory, which we must not
|
||||
|
do if we are invoked by a signal handler. So provide our own |
||||
|
sort. */ |
||||
|
|
||||
|
static void |
||||
|
swap (char *a, char *b, size_t size) |
||||
|
{ |
||||
|
size_t i; |
||||
|
|
||||
|
for (i = 0; i < size; i++, a++, b++) |
||||
|
{ |
||||
|
char t; |
||||
|
|
||||
|
t = *a; |
||||
|
*a = *b; |
||||
|
*b = t; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void |
||||
|
backtrace_qsort (void *basearg, size_t count, size_t size, |
||||
|
int (*compar) (const void *, const void *)) |
||||
|
{ |
||||
|
char *base = (char *) basearg; |
||||
|
size_t i; |
||||
|
size_t mid; |
||||
|
|
||||
|
tail_recurse: |
||||
|
if (count < 2) |
||||
|
return; |
||||
|
|
||||
|
/* The symbol table and DWARF tables, which is all we use this
|
||||
|
routine for, tend to be roughly sorted. Pick the middle element |
||||
|
in the array as our pivot point, so that we are more likely to |
||||
|
cut the array in half for each recursion step. */ |
||||
|
swap (base, base + (count / 2) * size, size); |
||||
|
|
||||
|
mid = 0; |
||||
|
for (i = 1; i < count; i++) |
||||
|
{ |
||||
|
if ((*compar) (base, base + i * size) > 0) |
||||
|
{ |
||||
|
++mid; |
||||
|
if (i != mid) |
||||
|
swap (base + mid * size, base + i * size, size); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (mid > 0) |
||||
|
swap (base, base + mid * size, size); |
||||
|
|
||||
|
/* Recurse with the smaller array, loop with the larger one. That
|
||||
|
ensures that our maximum stack depth is log count. */ |
||||
|
if (2 * mid < count) |
||||
|
{ |
||||
|
backtrace_qsort (base, mid, size, compar); |
||||
|
base += (mid + 1) * size; |
||||
|
count -= mid + 1; |
||||
|
goto tail_recurse; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
backtrace_qsort (base + (mid + 1) * size, count - (mid + 1), |
||||
|
size, compar); |
||||
|
count = mid; |
||||
|
goto tail_recurse; |
||||
|
} |
||||
|
} |
@ -0,0 +1,72 @@ |
|||||
|
/* state.c -- Create the backtrace state.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <string.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "backtrace-supported.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* Create the backtrace state. This will then be passed to all the
|
||||
|
other routines. */ |
||||
|
|
||||
|
struct backtrace_state * |
||||
|
backtrace_create_state (const char *filename, int threaded, |
||||
|
backtrace_error_callback error_callback, |
||||
|
void *data) |
||||
|
{ |
||||
|
struct backtrace_state init_state; |
||||
|
struct backtrace_state *state; |
||||
|
|
||||
|
#ifndef HAVE_SYNC_FUNCTIONS |
||||
|
if (threaded) |
||||
|
{ |
||||
|
error_callback (data, "backtrace library does not support threads", 0); |
||||
|
return NULL; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
memset (&init_state, 0, sizeof init_state); |
||||
|
init_state.filename = filename; |
||||
|
init_state.threaded = threaded; |
||||
|
|
||||
|
state = ((struct backtrace_state *) |
||||
|
backtrace_alloc (&init_state, sizeof *state, error_callback, data)); |
||||
|
if (state == NULL) |
||||
|
return NULL; |
||||
|
*state = init_state; |
||||
|
|
||||
|
return state; |
||||
|
} |
@ -0,0 +1,137 @@ |
|||||
|
/* stest.c -- Test for libbacktrace internal sort function
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* Test the local qsort implementation. */ |
||||
|
|
||||
|
#define MAX 10 |
||||
|
|
||||
|
struct test |
||||
|
{ |
||||
|
size_t count; |
||||
|
int input[MAX]; |
||||
|
int output[MAX]; |
||||
|
}; |
||||
|
|
||||
|
static struct test tests[] = |
||||
|
{ |
||||
|
{ |
||||
|
10, |
||||
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, |
||||
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } |
||||
|
}, |
||||
|
{ |
||||
|
9, |
||||
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, |
||||
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9 } |
||||
|
}, |
||||
|
{ |
||||
|
10, |
||||
|
{ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 }, |
||||
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, |
||||
|
}, |
||||
|
{ |
||||
|
9, |
||||
|
{ 9, 8, 7, 6, 5, 4, 3, 2, 1 }, |
||||
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9 }, |
||||
|
}, |
||||
|
{ |
||||
|
10, |
||||
|
{ 2, 4, 6, 8, 10, 1, 3, 5, 7, 9 }, |
||||
|
{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, |
||||
|
}, |
||||
|
{ |
||||
|
5, |
||||
|
{ 4, 5, 3, 1, 2 }, |
||||
|
{ 1, 2, 3, 4, 5 }, |
||||
|
}, |
||||
|
{ |
||||
|
5, |
||||
|
{ 1, 1, 1, 1, 1 }, |
||||
|
{ 1, 1, 1, 1, 1 }, |
||||
|
}, |
||||
|
{ |
||||
|
5, |
||||
|
{ 1, 1, 2, 1, 1 }, |
||||
|
{ 1, 1, 1, 1, 2 }, |
||||
|
}, |
||||
|
{ |
||||
|
5, |
||||
|
{ 2, 1, 1, 1, 1 }, |
||||
|
{ 1, 1, 1, 1, 2 }, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
static int |
||||
|
compare (const void *a, const void *b) |
||||
|
{ |
||||
|
const int *ai = (const int *) a; |
||||
|
const int *bi = (const int *) b; |
||||
|
|
||||
|
return *ai - *bi; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
main (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) |
||||
|
{ |
||||
|
int failures; |
||||
|
size_t i; |
||||
|
int a[MAX]; |
||||
|
|
||||
|
failures = 0; |
||||
|
for (i = 0; i < sizeof tests / sizeof tests[0]; i++) |
||||
|
{ |
||||
|
memcpy (a, tests[i].input, tests[i].count * sizeof (int)); |
||||
|
backtrace_qsort (a, tests[i].count, sizeof (int), compare); |
||||
|
if (memcmp (a, tests[i].output, tests[i].count * sizeof (int)) != 0) |
||||
|
{ |
||||
|
size_t j; |
||||
|
|
||||
|
fprintf (stderr, "test %d failed:", (int) i); |
||||
|
for (j = 0; j < tests[i].count; j++) |
||||
|
fprintf (stderr, " %d", a[j]); |
||||
|
fprintf (stderr, "\n"); |
||||
|
++failures; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
exit (failures > 0 ? EXIT_FAILURE : EXIT_SUCCESS); |
||||
|
} |
@ -0,0 +1,234 @@ |
|||||
|
/* testlib.c -- test functions for libbacktrace library
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#include "filenames.h" |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
|
||||
|
#include "testlib.h" |
||||
|
|
||||
|
/* The backtrace state. */ |
||||
|
|
||||
|
void *state; |
||||
|
|
||||
|
/* The number of failures. */ |
||||
|
|
||||
|
int failures; |
||||
|
|
||||
|
/* Return the base name in a path. */ |
||||
|
|
||||
|
const char * |
||||
|
base (const char *p) |
||||
|
{ |
||||
|
const char *last; |
||||
|
const char *s; |
||||
|
|
||||
|
last = NULL; |
||||
|
for (s = p; *s != '\0'; ++s) |
||||
|
{ |
||||
|
if (IS_DIR_SEPARATOR (*s)) |
||||
|
last = s + 1; |
||||
|
} |
||||
|
return last != NULL ? last : p; |
||||
|
} |
||||
|
|
||||
|
/* Check an entry in a struct info array. */ |
||||
|
|
||||
|
void |
||||
|
check (const char *name, int index, const struct info *all, int want_lineno, |
||||
|
const char *want_function, const char *want_file, int *failed) |
||||
|
{ |
||||
|
if (*failed) |
||||
|
return; |
||||
|
if (all[index].filename == NULL || all[index].function == NULL) |
||||
|
{ |
||||
|
fprintf (stderr, "%s: [%d]: missing file name or function name\n", |
||||
|
name, index); |
||||
|
*failed = 1; |
||||
|
return; |
||||
|
} |
||||
|
if (strcmp (base (all[index].filename), want_file) != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index, |
||||
|
all[index].filename, want_file); |
||||
|
*failed = 1; |
||||
|
} |
||||
|
if (all[index].lineno != want_lineno) |
||||
|
{ |
||||
|
fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index, |
||||
|
all[index].lineno, want_lineno); |
||||
|
*failed = 1; |
||||
|
} |
||||
|
if (strcmp (all[index].function, want_function) != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index, |
||||
|
all[index].function, want_function); |
||||
|
*failed = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* The backtrace callback function. */ |
||||
|
|
||||
|
int |
||||
|
callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, |
||||
|
const char *filename, int lineno, const char *function) |
||||
|
{ |
||||
|
struct bdata *data = (struct bdata *) vdata; |
||||
|
struct info *p; |
||||
|
|
||||
|
if (data->index >= data->max) |
||||
|
{ |
||||
|
fprintf (stderr, "callback_one: callback called too many times\n"); |
||||
|
data->failed = 1; |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
p = &data->all[data->index]; |
||||
|
if (filename == NULL) |
||||
|
p->filename = NULL; |
||||
|
else |
||||
|
{ |
||||
|
p->filename = strdup (filename); |
||||
|
assert (p->filename != NULL); |
||||
|
} |
||||
|
p->lineno = lineno; |
||||
|
if (function == NULL) |
||||
|
p->function = NULL; |
||||
|
else |
||||
|
{ |
||||
|
p->function = strdup (function); |
||||
|
assert (p->function != NULL); |
||||
|
} |
||||
|
++data->index; |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* An error callback passed to backtrace. */ |
||||
|
|
||||
|
void |
||||
|
error_callback_one (void *vdata, const char *msg, int errnum) |
||||
|
{ |
||||
|
struct bdata *data = (struct bdata *) vdata; |
||||
|
|
||||
|
fprintf (stderr, "%s", msg); |
||||
|
if (errnum > 0) |
||||
|
fprintf (stderr, ": %s", strerror (errnum)); |
||||
|
fprintf (stderr, "\n"); |
||||
|
data->failed = 1; |
||||
|
} |
||||
|
|
||||
|
/* The backtrace_simple callback function. */ |
||||
|
|
||||
|
int |
||||
|
callback_two (void *vdata, uintptr_t pc) |
||||
|
{ |
||||
|
struct sdata *data = (struct sdata *) vdata; |
||||
|
|
||||
|
if (data->index >= data->max) |
||||
|
{ |
||||
|
fprintf (stderr, "callback_two: callback called too many times\n"); |
||||
|
data->failed = 1; |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
data->addrs[data->index] = pc; |
||||
|
++data->index; |
||||
|
|
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
/* An error callback passed to backtrace_simple. */ |
||||
|
|
||||
|
void |
||||
|
error_callback_two (void *vdata, const char *msg, int errnum) |
||||
|
{ |
||||
|
struct sdata *data = (struct sdata *) vdata; |
||||
|
|
||||
|
fprintf (stderr, "%s", msg); |
||||
|
if (errnum > 0) |
||||
|
fprintf (stderr, ": %s", strerror (errnum)); |
||||
|
fprintf (stderr, "\n"); |
||||
|
data->failed = 1; |
||||
|
} |
||||
|
|
||||
|
/* The backtrace_syminfo callback function. */ |
||||
|
|
||||
|
void |
||||
|
callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED, |
||||
|
const char *symname, uintptr_t symval, |
||||
|
uintptr_t symsize) |
||||
|
{ |
||||
|
struct symdata *data = (struct symdata *) vdata; |
||||
|
|
||||
|
if (symname == NULL) |
||||
|
data->name = NULL; |
||||
|
else |
||||
|
{ |
||||
|
data->name = strdup (symname); |
||||
|
assert (data->name != NULL); |
||||
|
} |
||||
|
data->val = symval; |
||||
|
data->size = symsize; |
||||
|
} |
||||
|
|
||||
|
/* The backtrace_syminfo error callback function. */ |
||||
|
|
||||
|
void |
||||
|
error_callback_three (void *vdata, const char *msg, int errnum) |
||||
|
{ |
||||
|
struct symdata *data = (struct symdata *) vdata; |
||||
|
|
||||
|
fprintf (stderr, "%s", msg); |
||||
|
if (errnum > 0) |
||||
|
fprintf (stderr, ": %s", strerror (errnum)); |
||||
|
fprintf (stderr, "\n"); |
||||
|
data->failed = 1; |
||||
|
} |
||||
|
|
||||
|
/* The backtrace_create_state error callback function. */ |
||||
|
|
||||
|
void |
||||
|
error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg, |
||||
|
int errnum) |
||||
|
{ |
||||
|
fprintf (stderr, "%s", msg); |
||||
|
if (errnum > 0) |
||||
|
fprintf (stderr, ": %s", strerror (errnum)); |
||||
|
fprintf (stderr, "\n"); |
||||
|
exit (EXIT_FAILURE); |
||||
|
} |
@ -0,0 +1,110 @@ |
|||||
|
/* testlib.h -- Header for test functions for libbacktrace library
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#ifndef LIBBACKTRACE_TESTLIB_H |
||||
|
#define LIBBACKTRACE_TESTLIB_H |
||||
|
|
||||
|
/* Portable attribute syntax. Actually some of these tests probably
|
||||
|
won't work if the attributes are not recognized. */ |
||||
|
|
||||
|
#ifndef GCC_VERSION |
||||
|
# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) |
||||
|
#endif |
||||
|
|
||||
|
#if (GCC_VERSION < 2007) |
||||
|
# define __attribute__(x) |
||||
|
#endif |
||||
|
|
||||
|
#ifndef ATTRIBUTE_UNUSED |
||||
|
# define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) |
||||
|
#endif |
||||
|
|
||||
|
/* Used to collect backtrace info. */ |
||||
|
|
||||
|
struct info |
||||
|
{ |
||||
|
char *filename; |
||||
|
int lineno; |
||||
|
char *function; |
||||
|
}; |
||||
|
|
||||
|
/* Passed to backtrace callback function. */ |
||||
|
|
||||
|
struct bdata |
||||
|
{ |
||||
|
struct info *all; |
||||
|
size_t index; |
||||
|
size_t max; |
||||
|
int failed; |
||||
|
}; |
||||
|
|
||||
|
/* Passed to backtrace_simple callback function. */ |
||||
|
|
||||
|
struct sdata |
||||
|
{ |
||||
|
uintptr_t *addrs; |
||||
|
size_t index; |
||||
|
size_t max; |
||||
|
int failed; |
||||
|
}; |
||||
|
|
||||
|
/* Passed to backtrace_syminfo callback function. */ |
||||
|
|
||||
|
struct symdata |
||||
|
{ |
||||
|
const char *name; |
||||
|
uintptr_t val, size; |
||||
|
int failed; |
||||
|
}; |
||||
|
|
||||
|
/* The backtrace state. */ |
||||
|
|
||||
|
extern void *state; |
||||
|
|
||||
|
/* The number of failures. */ |
||||
|
|
||||
|
extern int failures; |
||||
|
|
||||
|
extern const char *base (const char *p); |
||||
|
extern void check (const char *name, int index, const struct info *all, |
||||
|
int want_lineno, const char *want_function, |
||||
|
const char *want_file, int *failed); |
||||
|
extern int callback_one (void *, uintptr_t, const char *, int, const char *); |
||||
|
extern void error_callback_one (void *, const char *, int); |
||||
|
extern int callback_two (void *, uintptr_t); |
||||
|
extern void error_callback_two (void *, const char *, int); |
||||
|
extern void callback_three (void *, uintptr_t, const char *, uintptr_t, |
||||
|
uintptr_t); |
||||
|
extern void error_callback_three (void *, const char *, int); |
||||
|
extern void error_callback_create (void *, const char *, int); |
||||
|
|
||||
|
#endif /* !defined(LIBBACKTRACE_TESTLIB_H) */ |
@ -0,0 +1,161 @@ |
|||||
|
/* ttest.c -- Test for libbacktrace library
|
||||
|
Copyright (C) 2017-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
/* Test using the libbacktrace library from multiple threads. */ |
||||
|
|
||||
|
#include <assert.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
|
||||
|
#include <pthread.h> |
||||
|
|
||||
|
#include "filenames.h" |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "backtrace-supported.h" |
||||
|
|
||||
|
#include "testlib.h" |
||||
|
|
||||
|
static int f2 (int) __attribute__ ((noinline)); |
||||
|
static int f3 (int, int) __attribute__ ((noinline)); |
||||
|
|
||||
|
/* Test that a simple backtrace works. This is called via
|
||||
|
pthread_create. It returns the number of failures, as void *. */ |
||||
|
|
||||
|
static void * |
||||
|
test1_thread (void *arg ATTRIBUTE_UNUSED) |
||||
|
{ |
||||
|
/* Returning a value here and elsewhere avoids a tailcall which
|
||||
|
would mess up the backtrace. */ |
||||
|
return (void *) (uintptr_t) (f2 (__LINE__) - 2); |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
f2 (int f1line) |
||||
|
{ |
||||
|
return f3 (f1line, __LINE__) + 2; |
||||
|
} |
||||
|
|
||||
|
static int |
||||
|
f3 (int f1line, int f2line) |
||||
|
{ |
||||
|
struct info all[20]; |
||||
|
struct bdata data; |
||||
|
int f3line; |
||||
|
int i; |
||||
|
|
||||
|
data.all = &all[0]; |
||||
|
data.index = 0; |
||||
|
data.max = 20; |
||||
|
data.failed = 0; |
||||
|
|
||||
|
f3line = __LINE__ + 1; |
||||
|
i = backtrace_full (state, 0, callback_one, error_callback_one, &data); |
||||
|
|
||||
|
if (i != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "test1: unexpected return value %d\n", i); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
if (data.index < 3) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test1: not enough frames; got %zu, expected at least 3\n", |
||||
|
data.index); |
||||
|
data.failed = 1; |
||||
|
} |
||||
|
|
||||
|
check ("test1", 0, all, f3line, "f3", "ttest.c", &data.failed); |
||||
|
check ("test1", 1, all, f2line, "f2", "ttest.c", &data.failed); |
||||
|
check ("test1", 2, all, f1line, "test1_thread", "ttest.c", &data.failed); |
||||
|
|
||||
|
return data.failed; |
||||
|
} |
||||
|
|
||||
|
/* Run the test with 10 threads simultaneously. */ |
||||
|
|
||||
|
#define THREAD_COUNT 10 |
||||
|
|
||||
|
static void test1 (void) __attribute__ ((unused)); |
||||
|
|
||||
|
static void |
||||
|
test1 (void) |
||||
|
{ |
||||
|
pthread_t atid[THREAD_COUNT]; |
||||
|
int i; |
||||
|
int errnum; |
||||
|
int this_fail; |
||||
|
void *ret; |
||||
|
|
||||
|
for (i = 0; i < THREAD_COUNT; i++) |
||||
|
{ |
||||
|
errnum = pthread_create (&atid[i], NULL, test1_thread, NULL); |
||||
|
if (errnum != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "pthread_create %d: %s\n", i, strerror (errnum)); |
||||
|
exit (EXIT_FAILURE); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
this_fail = 0; |
||||
|
for (i = 0; i < THREAD_COUNT; i++) |
||||
|
{ |
||||
|
errnum = pthread_join (atid[i], &ret); |
||||
|
if (errnum != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "pthread_join %d: %s\n", i, strerror (errnum)); |
||||
|
exit (EXIT_FAILURE); |
||||
|
} |
||||
|
this_fail += (int) (uintptr_t) ret; |
||||
|
} |
||||
|
|
||||
|
printf ("%s: threaded backtrace_full noinline\n", this_fail > 0 ? "FAIL" : "PASS"); |
||||
|
|
||||
|
failures += this_fail; |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
main (int argc ATTRIBUTE_UNUSED, char **argv) |
||||
|
{ |
||||
|
state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, |
||||
|
error_callback_create, NULL); |
||||
|
|
||||
|
#if BACKTRACE_SUPPORTED |
||||
|
#if BACKTRACE_SUPPORTS_THREADS |
||||
|
test1 (); |
||||
|
#endif |
||||
|
#endif |
||||
|
|
||||
|
exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); |
||||
|
} |
@ -0,0 +1,65 @@ |
|||||
|
/* unknown.c -- used when backtrace configury does not know file format.
|
||||
|
Copyright (C) 2012-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "internal.h" |
||||
|
|
||||
|
/* A trivial routine that always fails to find fileline data. */ |
||||
|
|
||||
|
static int |
||||
|
unknown_fileline (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
uintptr_t pc, backtrace_full_callback callback, |
||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
||||
|
void *data) |
||||
|
|
||||
|
{ |
||||
|
return callback (data, pc, NULL, 0, NULL); |
||||
|
} |
||||
|
|
||||
|
/* Initialize the backtrace data when we don't know how to read the
|
||||
|
debug info. */ |
||||
|
|
||||
|
int |
||||
|
backtrace_initialize (struct backtrace_state *state ATTRIBUTE_UNUSED, |
||||
|
const char *filename ATTRIBUTE_UNUSED, |
||||
|
int descriptor ATTRIBUTE_UNUSED, |
||||
|
backtrace_error_callback error_callback ATTRIBUTE_UNUSED, |
||||
|
void *data ATTRIBUTE_UNUSED, fileline *fileline_fn) |
||||
|
{ |
||||
|
state->fileline_data = NULL; |
||||
|
*fileline_fn = unknown_fileline; |
||||
|
return 1; |
||||
|
} |
File diff suppressed because it is too large
@ -0,0 +1,537 @@ |
|||||
|
/* ztest.c -- Test for libbacktrace inflate code.
|
||||
|
Copyright (C) 2017-2018 Free Software Foundation, Inc. |
||||
|
Written by Ian Lance Taylor, Google. |
||||
|
|
||||
|
Redistribution and use in source and binary forms, with or without |
||||
|
modification, are permitted provided that the following conditions are |
||||
|
met: |
||||
|
|
||||
|
(1) Redistributions of source code must retain the above copyright |
||||
|
notice, this list of conditions and the following disclaimer. |
||||
|
|
||||
|
(2) Redistributions in binary form must reproduce the above copyright |
||||
|
notice, this list of conditions and the following disclaimer in |
||||
|
the documentation and/or other materials provided with the |
||||
|
distribution. |
||||
|
|
||||
|
(3) The name of the author may not be used to |
||||
|
endorse or promote products derived from this software without |
||||
|
specific prior written permission. |
||||
|
|
||||
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
||||
|
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
|
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
|
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
|
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
|
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
|
POSSIBILITY OF SUCH DAMAGE. */ |
||||
|
|
||||
|
#include "config.h" |
||||
|
|
||||
|
#include <errno.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <time.h> |
||||
|
#include <sys/types.h> |
||||
|
#include <sys/stat.h> |
||||
|
|
||||
|
#ifdef HAVE_ZLIB |
||||
|
#include <zlib.h> |
||||
|
#endif |
||||
|
|
||||
|
#include "backtrace.h" |
||||
|
#include "backtrace-supported.h" |
||||
|
|
||||
|
#include "internal.h" |
||||
|
#include "testlib.h" |
||||
|
|
||||
|
#ifndef HAVE_CLOCK_GETTIME |
||||
|
|
||||
|
typedef int xclockid_t; |
||||
|
|
||||
|
static int |
||||
|
xclock_gettime (xclockid_t id ATTRIBUTE_UNUSED, |
||||
|
struct timespec *ts ATTRIBUTE_UNUSED) |
||||
|
{ |
||||
|
errno = EINVAL; |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
#define clockid_t xclockid_t |
||||
|
#define clock_gettime xclock_gettime |
||||
|
#undef CLOCK_REALTIME |
||||
|
#define CLOCK_REALTIME 0 |
||||
|
|
||||
|
#endif /* !defined(HAVE_CLOCK_GETTIME) */ |
||||
|
|
||||
|
#ifdef CLOCK_PROCESS_CPUTIME_ID |
||||
|
#define ZLIB_CLOCK_GETTIME_ARG CLOCK_PROCESS_CPUTIME_ID |
||||
|
#else |
||||
|
#define ZLIB_CLOCK_GETTIME_ARG CLOCK_REALTIME |
||||
|
#endif |
||||
|
|
||||
|
/* Some tests for the local zlib inflation code. */ |
||||
|
|
||||
|
struct zlib_test |
||||
|
{ |
||||
|
const char *name; |
||||
|
const char *uncompressed; |
||||
|
size_t uncompressed_len; |
||||
|
const char *compressed; |
||||
|
size_t compressed_len; |
||||
|
}; |
||||
|
|
||||
|
/* Error callback. */ |
||||
|
|
||||
|
static void |
||||
|
error_callback_compress (void *vdata, const char *msg, int errnum) |
||||
|
{ |
||||
|
fprintf (stderr, "%s", msg); |
||||
|
if (errnum > 0) |
||||
|
fprintf (stderr, ": %s", strerror (errnum)); |
||||
|
fprintf (stderr, "\n"); |
||||
|
exit (EXIT_FAILURE); |
||||
|
} |
||||
|
|
||||
|
static const struct zlib_test tests[] = |
||||
|
{ |
||||
|
{ |
||||
|
"empty", |
||||
|
"", |
||||
|
0, |
||||
|
"\x78\x9c\x03\x00\x00\x00\x00\x01", |
||||
|
8, |
||||
|
}, |
||||
|
{ |
||||
|
"hello", |
||||
|
"hello, world\n", |
||||
|
0, |
||||
|
("\x78\x9c\xca\x48\xcd\xc9\xc9\xd7\x51\x28\xcf" |
||||
|
"\x2f\xca\x49\xe1\x02\x04\x00\x00\xff\xff\x21\xe7\x04\x93"), |
||||
|
25, |
||||
|
}, |
||||
|
{ |
||||
|
"goodbye", |
||||
|
"goodbye, world", |
||||
|
0, |
||||
|
("\x78\x9c\x4b\xcf\xcf\x4f\x49\xaa" |
||||
|
"\x4c\xd5\x51\x28\xcf\x2f\xca\x49" |
||||
|
"\x01\x00\x28\xa5\x05\x5e"), |
||||
|
22, |
||||
|
}, |
||||
|
{ |
||||
|
"ranges", |
||||
|
("\xcc\x11\x00\x00\x00\x00\x00\x00\xd5\x13\x00\x00\x00\x00\x00\x00" |
||||
|
"\x1c\x14\x00\x00\x00\x00\x00\x00\x72\x14\x00\x00\x00\x00\x00\x00" |
||||
|
"\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00" |
||||
|
"\x0c\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00" |
||||
|
"\x29\x14\x00\x00\x00\x00\x00\x00\x4e\x14\x00\x00\x00\x00\x00\x00" |
||||
|
"\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00" |
||||
|
"\x67\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00" |
||||
|
"\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\x5f\x0b\x00\x00\x00\x00\x00\x00\x6c\x0b\x00\x00\x00\x00\x00\x00" |
||||
|
"\x7d\x0b\x00\x00\x00\x00\x00\x00\x7e\x0c\x00\x00\x00\x00\x00\x00" |
||||
|
"\x38\x0f\x00\x00\x00\x00\x00\x00\x5c\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\x83\x0c\x00\x00\x00\x00\x00\x00\xfa\x0c\x00\x00\x00\x00\x00\x00" |
||||
|
"\xfd\x0d\x00\x00\x00\x00\x00\x00\xef\x0e\x00\x00\x00\x00\x00\x00" |
||||
|
"\x14\x0f\x00\x00\x00\x00\x00\x00\x38\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\xfd\x0d\x00\x00\x00\x00\x00\x00\xd8\x0e\x00\x00\x00\x00\x00\x00" |
||||
|
"\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\xfa\x0c\x00\x00\x00\x00\x00\x00\xea\x0d\x00\x00\x00\x00\x00\x00" |
||||
|
"\xef\x0e\x00\x00\x00\x00\x00\x00\x14\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\x5c\x0f\x00\x00\x00\x00\x00\x00\x9f\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\xac\x0f\x00\x00\x00\x00\x00\x00\xdb\x0f\x00\x00\x00\x00\x00\x00" |
||||
|
"\xff\x0f\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\x60\x11\x00\x00\x00\x00\x00\x00\xd1\x16\x00\x00\x00\x00\x00\x00" |
||||
|
"\x40\x0b\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\x7a\x00\x00\x00\x00\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\x7a\x00\x00\x00\x00\x00\x00\x00\xa9\x00\x00\x00\x00\x00\x00\x00" |
||||
|
"\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00" |
||||
|
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), |
||||
|
672, |
||||
|
("\x78\x9c\x3b\x23\xc8\x00\x06\x57\x85\x21\xb4\x8c\x08\x84\x2e\x82" |
||||
|
"\xd2\x73\xa1\xf4\x55\x28\x8d\x0e\x7e\x0b\x41\x68\x4e\xa8\x7e\x1e" |
||||
|
"\x28\x7d\x1a\x4a\x6b\x42\xf5\xf9\x91\x69\x5e\x3a\x9a\x79\x84\xf4" |
||||
|
"\xc7\x73\x43\xe8\x1c\x28\x5d\x0b\xa5\xeb\x78\x20\xb4\x05\x3f\x84" |
||||
|
"\x8e\xe1\xc7\xae\xbf\x19\xaa\xee\x17\x94\xfe\xcb\x0b\xa1\xdf\xf3" |
||||
|
"\x41\x68\x11\x7e\x54\x73\xe6\x43\xe9\x35\x50\xfa\x36\x94\xfe\x8f" |
||||
|
"\xc3\x7c\x98\x79\x37\xf8\xc8\xd3\x0f\x73\xd7\x2b\x1c\xee\x8a\x21" |
||||
|
"\xd2\x5d\x3a\x02\xd8\xcd\x4f\x80\xa6\x87\x8b\x62\x10\xda\x81\x1b" |
||||
|
"\xbf\xfa\x2a\x28\xbd\x0d\x4a\xcf\x67\x84\xd0\xcb\x19\xf1\xab\x5f" |
||||
|
"\x49\xa4\x7a\x00\x48\x97\x29\xd4"), |
||||
|
152, |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
/* Test the hand coded samples. */ |
||||
|
|
||||
|
static void |
||||
|
test_samples (struct backtrace_state *state) |
||||
|
{ |
||||
|
size_t i; |
||||
|
|
||||
|
for (i = 0; i < sizeof tests / sizeof tests[0]; ++i) |
||||
|
{ |
||||
|
char *p; |
||||
|
size_t v; |
||||
|
size_t j; |
||||
|
unsigned char *uncompressed; |
||||
|
size_t uncompressed_len; |
||||
|
|
||||
|
p = malloc (12 + tests[i].compressed_len); |
||||
|
memcpy (p, "ZLIB", 4); |
||||
|
v = tests[i].uncompressed_len; |
||||
|
if (v == 0) |
||||
|
v = strlen (tests[i].uncompressed); |
||||
|
for (j = 0; j < 8; ++j) |
||||
|
p[j + 4] = (v >> ((7 - j) * 8)) & 0xff; |
||||
|
memcpy (p + 12, tests[i].compressed, tests[i].compressed_len); |
||||
|
uncompressed = NULL; |
||||
|
uncompressed_len = 0; |
||||
|
if (!backtrace_uncompress_zdebug (state, (unsigned char *) p, |
||||
|
tests[i].compressed_len + 12, |
||||
|
error_callback_compress, NULL, |
||||
|
&uncompressed, &uncompressed_len)) |
||||
|
{ |
||||
|
fprintf (stderr, "test %s: uncompress failed\n", tests[i].name); |
||||
|
++failures; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
if (uncompressed_len != v) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"test %s: got uncompressed length %zu, want %zu\n", |
||||
|
tests[i].name, uncompressed_len, v); |
||||
|
++failures; |
||||
|
} |
||||
|
else if (memcmp (tests[i].uncompressed, uncompressed, v) != 0) |
||||
|
{ |
||||
|
size_t j; |
||||
|
|
||||
|
fprintf (stderr, "test %s: uncompressed data mismatch\n", |
||||
|
tests[i].name); |
||||
|
for (j = 0; j < v; ++j) |
||||
|
if (tests[i].uncompressed[j] != uncompressed[j]) |
||||
|
fprintf (stderr, " %zu: got %#x want %#x\n", j, |
||||
|
uncompressed[j], tests[i].uncompressed[j]); |
||||
|
++failures; |
||||
|
} |
||||
|
else |
||||
|
printf ("PASS: inflate %s\n", tests[i].name); |
||||
|
|
||||
|
backtrace_free (state, uncompressed, uncompressed_len, |
||||
|
error_callback_compress, NULL); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#ifdef HAVE_ZLIB |
||||
|
|
||||
|
/* Given a set of TRIALS timings, discard the lowest and highest
|
||||
|
values and return the mean average of the rest. */ |
||||
|
|
||||
|
static size_t |
||||
|
average_time (const size_t *times, size_t trials) |
||||
|
{ |
||||
|
size_t imax; |
||||
|
size_t max; |
||||
|
size_t imin; |
||||
|
size_t min; |
||||
|
size_t i; |
||||
|
size_t sum; |
||||
|
|
||||
|
imin = 0; |
||||
|
imax = 0; |
||||
|
min = times[0]; |
||||
|
max = times[0]; |
||||
|
for (i = 1; i < trials; ++i) |
||||
|
{ |
||||
|
if (times[i] < min) |
||||
|
{ |
||||
|
imin = i; |
||||
|
min = times[i]; |
||||
|
} |
||||
|
if (times[i] > max) |
||||
|
{ |
||||
|
imax = i; |
||||
|
max = times[i]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
sum = 0; |
||||
|
for (i = 0; i < trials; ++i) |
||||
|
{ |
||||
|
if (i != imax && i != imin) |
||||
|
sum += times[i]; |
||||
|
} |
||||
|
return sum / (trials - 2); |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
|
||||
|
/* Test a larger text, if available. */ |
||||
|
|
||||
|
static void |
||||
|
test_large (struct backtrace_state *state) |
||||
|
{ |
||||
|
#ifdef HAVE_ZLIB |
||||
|
unsigned char *orig_buf; |
||||
|
size_t orig_bufsize; |
||||
|
size_t i; |
||||
|
char *compressed_buf; |
||||
|
size_t compressed_bufsize; |
||||
|
unsigned long compress_sizearg; |
||||
|
unsigned char *uncompressed_buf; |
||||
|
size_t uncompressed_bufsize; |
||||
|
int r; |
||||
|
clockid_t cid; |
||||
|
struct timespec ts1; |
||||
|
struct timespec ts2; |
||||
|
size_t ctime; |
||||
|
size_t ztime; |
||||
|
const size_t trials = 16; |
||||
|
size_t ctimes[16]; |
||||
|
size_t ztimes[16]; |
||||
|
static const char * const names[] = { |
||||
|
"Mark.Twain-Tom.Sawyer.txt", |
||||
|
"../libgo/go/compress/testdata/Mark.Twain-Tom.Sawyer.txt" |
||||
|
}; |
||||
|
|
||||
|
orig_buf = NULL; |
||||
|
orig_bufsize = 0; |
||||
|
uncompressed_buf = NULL; |
||||
|
compressed_buf = NULL; |
||||
|
|
||||
|
for (i = 0; i < sizeof names / sizeof names[0]; ++i) |
||||
|
{ |
||||
|
size_t len; |
||||
|
char *namebuf; |
||||
|
FILE *e; |
||||
|
struct stat st; |
||||
|
char *rbuf; |
||||
|
size_t got; |
||||
|
|
||||
|
len = strlen (SRCDIR) + strlen (names[i]) + 2; |
||||
|
namebuf = malloc (len); |
||||
|
if (namebuf == NULL) |
||||
|
{ |
||||
|
perror ("malloc"); |
||||
|
goto fail; |
||||
|
} |
||||
|
snprintf (namebuf, len, "%s/%s", SRCDIR, names[i]); |
||||
|
e = fopen (namebuf, "r"); |
||||
|
free (namebuf); |
||||
|
if (e == NULL) |
||||
|
continue; |
||||
|
if (fstat (fileno (e), &st) < 0) |
||||
|
{ |
||||
|
perror ("fstat"); |
||||
|
fclose (e); |
||||
|
continue; |
||||
|
} |
||||
|
rbuf = malloc (st.st_size); |
||||
|
if (rbuf == NULL) |
||||
|
{ |
||||
|
perror ("malloc"); |
||||
|
goto fail; |
||||
|
} |
||||
|
got = fread (rbuf, 1, st.st_size, e); |
||||
|
fclose (e); |
||||
|
if (got > 0) |
||||
|
{ |
||||
|
orig_buf = rbuf; |
||||
|
orig_bufsize = got; |
||||
|
break; |
||||
|
} |
||||
|
free (rbuf); |
||||
|
} |
||||
|
|
||||
|
if (orig_buf == NULL) |
||||
|
{ |
||||
|
/* We couldn't find an input file. */ |
||||
|
printf ("UNSUPPORTED: inflate large\n"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
compressed_bufsize = compressBound (orig_bufsize) + 12; |
||||
|
compressed_buf = malloc (compressed_bufsize); |
||||
|
if (compressed_buf == NULL) |
||||
|
{ |
||||
|
perror ("malloc"); |
||||
|
goto fail; |
||||
|
} |
||||
|
|
||||
|
compress_sizearg = compressed_bufsize - 12; |
||||
|
r = compress (compressed_buf + 12, &compress_sizearg, |
||||
|
orig_buf, orig_bufsize); |
||||
|
if (r != Z_OK) |
||||
|
{ |
||||
|
fprintf (stderr, "zlib compress failed: %d\n", r); |
||||
|
goto fail; |
||||
|
} |
||||
|
|
||||
|
compressed_bufsize = compress_sizearg + 12; |
||||
|
|
||||
|
/* Prepare the header that our library expects. */ |
||||
|
memcpy (compressed_buf, "ZLIB", 4); |
||||
|
for (i = 0; i < 8; ++i) |
||||
|
compressed_buf[i + 4] = (orig_bufsize >> ((7 - i) * 8)) & 0xff; |
||||
|
|
||||
|
uncompressed_buf = malloc (orig_bufsize); |
||||
|
if (uncompressed_buf == NULL) |
||||
|
{ |
||||
|
perror ("malloc"); |
||||
|
goto fail; |
||||
|
} |
||||
|
uncompressed_bufsize = orig_bufsize; |
||||
|
|
||||
|
if (!backtrace_uncompress_zdebug (state, compressed_buf, compressed_bufsize, |
||||
|
error_callback_compress, NULL, |
||||
|
&uncompressed_buf, &uncompressed_bufsize)) |
||||
|
{ |
||||
|
fprintf (stderr, "inflate large: backtrace_uncompress_zdebug failed\n"); |
||||
|
goto fail; |
||||
|
} |
||||
|
|
||||
|
if (uncompressed_bufsize != orig_bufsize) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"inflate large: got uncompressed length %zu, want %zu\n", |
||||
|
uncompressed_bufsize, orig_bufsize); |
||||
|
goto fail; |
||||
|
} |
||||
|
|
||||
|
if (memcmp (uncompressed_buf, orig_buf, uncompressed_bufsize) != 0) |
||||
|
{ |
||||
|
fprintf (stderr, "inflate large: uncompressed data mismatch\n"); |
||||
|
goto fail; |
||||
|
} |
||||
|
|
||||
|
printf ("PASS: inflate large\n"); |
||||
|
|
||||
|
for (i = 0; i < trials; ++i) |
||||
|
{ |
||||
|
unsigned long uncompress_sizearg; |
||||
|
|
||||
|
cid = ZLIB_CLOCK_GETTIME_ARG; |
||||
|
if (clock_gettime (cid, &ts1) < 0) |
||||
|
{ |
||||
|
if (errno == EINVAL) |
||||
|
return; |
||||
|
perror ("clock_gettime"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (!backtrace_uncompress_zdebug (state, compressed_buf, |
||||
|
compressed_bufsize, |
||||
|
error_callback_compress, NULL, |
||||
|
&uncompressed_buf, |
||||
|
&uncompressed_bufsize)) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
("inflate large: " |
||||
|
"benchmark backtrace_uncompress_zdebug failed\n")); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (clock_gettime (cid, &ts2) < 0) |
||||
|
{ |
||||
|
perror ("clock_gettime"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ctime = (ts2.tv_sec - ts1.tv_sec) * 1000000000; |
||||
|
ctime += ts2.tv_nsec - ts1.tv_nsec; |
||||
|
ctimes[i] = ctime; |
||||
|
|
||||
|
if (clock_gettime (cid, &ts1) < 0) |
||||
|
{ |
||||
|
perror("clock_gettime"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
uncompress_sizearg = uncompressed_bufsize; |
||||
|
r = uncompress (uncompressed_buf, &uncompress_sizearg, |
||||
|
compressed_buf + 12, compressed_bufsize - 12); |
||||
|
|
||||
|
if (clock_gettime (cid, &ts2) < 0) |
||||
|
{ |
||||
|
perror ("clock_gettime"); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
if (r != Z_OK) |
||||
|
{ |
||||
|
fprintf (stderr, |
||||
|
"inflate large: benchmark zlib uncompress failed: %d\n", |
||||
|
r); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
ztime = (ts2.tv_sec - ts1.tv_sec) * 1000000000; |
||||
|
ztime += ts2.tv_nsec - ts1.tv_nsec; |
||||
|
ztimes[i] = ztime; |
||||
|
} |
||||
|
|
||||
|
/* Toss the highest and lowest times and average the rest. */ |
||||
|
ctime = average_time (ctimes, trials); |
||||
|
ztime = average_time (ztimes, trials); |
||||
|
|
||||
|
printf ("backtrace: %zu ns\n", ctime); |
||||
|
printf ("zlib : %zu ns\n", ztime); |
||||
|
printf ("ratio : %g\n", (double) ztime / (double) ctime); |
||||
|
|
||||
|
return; |
||||
|
|
||||
|
fail: |
||||
|
printf ("FAIL: inflate large\n"); |
||||
|
++failures; |
||||
|
|
||||
|
if (orig_buf != NULL) |
||||
|
free (orig_buf); |
||||
|
if (compressed_buf != NULL) |
||||
|
free (compressed_buf); |
||||
|
if (uncompressed_buf != NULL) |
||||
|
free (uncompressed_buf); |
||||
|
|
||||
|
#else /* !HAVE_ZLIB */ |
||||
|
|
||||
|
printf ("UNSUPPORTED: inflate large\n"); |
||||
|
|
||||
|
#endif /* !HAVE_ZLIB */ |
||||
|
} |
||||
|
|
||||
|
int |
||||
|
main (int argc ATTRIBUTE_UNUSED, char **argv) |
||||
|
{ |
||||
|
struct backtrace_state *state; |
||||
|
|
||||
|
state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, |
||||
|
error_callback_create, NULL); |
||||
|
|
||||
|
test_samples (state); |
||||
|
test_large (state); |
||||
|
|
||||
|
exit (failures != 0 ? EXIT_FAILURE : EXIT_SUCCESS); |
||||
|
} |
@ -0,0 +1,157 @@ |
|||||
|
*.bc |
||||
|
*.cmake |
||||
|
*.dSYM |
||||
|
*.done |
||||
|
*.final |
||||
|
*.gcda |
||||
|
*.gcno |
||||
|
*.i |
||||
|
*.la |
||||
|
*.lo |
||||
|
*.log |
||||
|
*.mem |
||||
|
*.nexe |
||||
|
*.o |
||||
|
*.plist |
||||
|
*.scan |
||||
|
*.sdf |
||||
|
*.status |
||||
|
*.tar.* |
||||
|
*.wasm |
||||
|
*.wast |
||||
|
*~ |
||||
|
.DS_Store |
||||
|
.deps |
||||
|
.dirstamp |
||||
|
.done |
||||
|
.libs |
||||
|
/bin/ |
||||
|
/obj/ |
||||
|
Build |
||||
|
INSTALL |
||||
|
Makefile |
||||
|
Makefile.in |
||||
|
Vagrantfile |
||||
|
aclocal.m4 |
||||
|
android-toolchain |
||||
|
android-toolchain-* |
||||
|
autom4te.cache |
||||
|
build |
||||
|
compile |
||||
|
confdefs.h |
||||
|
config.* |
||||
|
configure |
||||
|
configure.lineno |
||||
|
coverage.info |
||||
|
depcomp |
||||
|
install-sh |
||||
|
libsodium-*.tar.bz2 |
||||
|
libsodium-*.tar.gz |
||||
|
libsodium-*.vcproj |
||||
|
libsodium-*.vcproj.filters |
||||
|
libsodium-*.vcxproj |
||||
|
libsodium-*.vcxproj.filters |
||||
|
libsodium-android-* |
||||
|
libsodium-ios |
||||
|
libsodium-js |
||||
|
libsodium-js-* |
||||
|
libsodium-nativeclient |
||||
|
libsodium-nativeclient-* |
||||
|
libsodium-osx |
||||
|
libsodium-uninstalled.pc |
||||
|
libsodium-win32 |
||||
|
libsodium-win64 |
||||
|
libsodium.pc |
||||
|
libtool |
||||
|
ltmain.sh |
||||
|
m4/argz.m4 |
||||
|
m4/libtool.m4 |
||||
|
m4/ltoptions.m4 |
||||
|
m4/ltsugar.m4 |
||||
|
m4/ltversion.m4 |
||||
|
m4/lt~obsolete.m4 |
||||
|
man/*.html |
||||
|
man/Makefile.in |
||||
|
missing |
||||
|
src/libsodium/*.def |
||||
|
src/libsodium/include/sodium/version.h |
||||
|
stamp-* |
||||
|
test-driver |
||||
|
test/default/browser |
||||
|
test/default/*.asm.js |
||||
|
test/default/*.res |
||||
|
test/default/*.trs |
||||
|
test/default/aead_aes256gcm |
||||
|
test/default/aead_chacha20poly1305 |
||||
|
test/default/aead_xchacha20poly1305 |
||||
|
test/default/auth |
||||
|
test/default/auth2 |
||||
|
test/default/auth3 |
||||
|
test/default/auth5 |
||||
|
test/default/auth6 |
||||
|
test/default/auth7 |
||||
|
test/default/box |
||||
|
test/default/box2 |
||||
|
test/default/box7 |
||||
|
test/default/box8 |
||||
|
test/default/box_easy |
||||
|
test/default/box_easy2 |
||||
|
test/default/box_seal |
||||
|
test/default/box_seed |
||||
|
test/default/chacha20 |
||||
|
test/default/codecs |
||||
|
test/default/core_ed25519 |
||||
|
test/default/core1 |
||||
|
test/default/core2 |
||||
|
test/default/core3 |
||||
|
test/default/core4 |
||||
|
test/default/core5 |
||||
|
test/default/core6 |
||||
|
test/default/ed25519_convert |
||||
|
test/default/generichash |
||||
|
test/default/generichash2 |
||||
|
test/default/generichash3 |
||||
|
test/default/hash |
||||
|
test/default/hash3 |
||||
|
test/default/kdf |
||||
|
test/default/keygen |
||||
|
test/default/kx |
||||
|
test/default/metamorphic |
||||
|
test/default/misuse |
||||
|
test/default/onetimeauth |
||||
|
test/default/onetimeauth2 |
||||
|
test/default/onetimeauth7 |
||||
|
test/default/pwhash_argon2i |
||||
|
test/default/pwhash_argon2id |
||||
|
test/default/pwhash_scrypt |
||||
|
test/default/pwhash_scrypt_ll |
||||
|
test/default/randombytes |
||||
|
test/default/scalarmult |
||||
|
test/default/scalarmult_ed25519 |
||||
|
test/default/scalarmult2 |
||||
|
test/default/scalarmult5 |
||||
|
test/default/scalarmult6 |
||||
|
test/default/scalarmult7 |
||||
|
test/default/secretbox |
||||
|
test/default/secretbox2 |
||||
|
test/default/secretbox7 |
||||
|
test/default/secretbox8 |
||||
|
test/default/secretbox_easy |
||||
|
test/default/secretbox_easy2 |
||||
|
test/default/secretstream |
||||
|
test/default/shorthash |
||||
|
test/default/sign |
||||
|
test/default/siphashx24 |
||||
|
test/default/sodium_core |
||||
|
test/default/sodium_utils |
||||
|
test/default/sodium_utils2 |
||||
|
test/default/sodium_utils3 |
||||
|
test/default/sodium_version |
||||
|
test/default/stream |
||||
|
test/default/stream2 |
||||
|
test/default/stream3 |
||||
|
test/default/stream4 |
||||
|
test/default/verify1 |
||||
|
test/default/xchacha20 |
||||
|
test/js.done |
||||
|
testing |
@ -0,0 +1,33 @@ |
|||||
|
sudo: false |
||||
|
|
||||
|
language: c |
||||
|
|
||||
|
os: |
||||
|
- linux |
||||
|
|
||||
|
compiler: |
||||
|
- clang |
||||
|
- gcc |
||||
|
- g++ |
||||
|
|
||||
|
install: |
||||
|
- ./autogen.sh |
||||
|
- env CC=tcc CFLAGS='-w' ./configure --prefix=/tmp --disable-dependency-tracking --disable-shared || cat config.log |
||||
|
- make -j $(nproc) && make check && make install |
||||
|
- env CC=tcc CPPFLAGS='-I/tmp/include' LDFLAGS='-L/tmp/lib' LD_LIBRARY_PATH='/tmp/lib' ./test/constcheck.sh |
||||
|
- make uninstall |
||||
|
- make distclean |
||||
|
|
||||
|
script: |
||||
|
- ./configure --disable-dependency-tracking |
||||
|
- if [ "$TRAVIS_OS_NAME" = 'linux' -a "$CC" = 'gcc' ]; then make -j $(nproc) CFLAGS='-g0' > /dev/null && cp src/libsodium/.libs/libsodium.so lib.so && make clean > /dev/null && make CFLAGS='-g0' CPPFLAGS='-DSODIUM_C99\(X\)=' > /dev/null && cp src/libsodium/.libs/libsodium.so lib-oldc.so && cmp lib.so lib-oldc.so && echo No binary changes && make clean > /dev/null ; fi |
||||
|
- make distcheck |
||||
|
- make distclean > /dev/null |
||||
|
- ./configure --disable-dependency-tracking --enable-minimal |
||||
|
- make check |
||||
|
- ( echo '#include <sodium.h>' ; echo 'int main(void) { return sodium_init(); }' ) > /tmp/main.c && gcc -Isrc/libsodium/include -Isrc/libsodium/include/sodium $(find src -name '*.c' -o -name '*.S') /tmp/main.c |
||||
|
|
||||
|
addons: |
||||
|
apt: |
||||
|
packages: |
||||
|
- tcc |
@ -0,0 +1,135 @@ |
|||||
|
|
||||
|
Designers |
||||
|
========= |
||||
|
|
||||
|
argon2 Alex Biryukov |
||||
|
Daniel Dinu |
||||
|
Dmitry Khovratovich |
||||
|
|
||||
|
blake2 Jean-Philippe Aumasson |
||||
|
Christian Winnerlein |
||||
|
Samuel Neves |
||||
|
Zooko Wilcox-O'Hearn |
||||
|
|
||||
|
chacha20 Daniel J. Bernstein |
||||
|
|
||||
|
chacha20poly1305 Adam Langley |
||||
|
Yoav Nir |
||||
|
|
||||
|
curve25519 Daniel J. Bernstein |
||||
|
|
||||
|
curve25519xsalsa20poly1305 Daniel J. Bernstein |
||||
|
|
||||
|
ed25519 Daniel J. Bernstein |
||||
|
Bo-Yin Yang |
||||
|
Niels Duif |
||||
|
Peter Schwabe |
||||
|
Tanja Lange |
||||
|
|
||||
|
poly1305 Daniel J. Bernstein |
||||
|
|
||||
|
salsa20 Daniel J. Bernstein |
||||
|
|
||||
|
scrypt Colin Percival |
||||
|
|
||||
|
siphash Jean-Philippe Aumasson |
||||
|
Daniel J. Bernstein |
||||
|
|
||||
|
Implementors |
||||
|
============ |
||||
|
|
||||
|
crypto_aead/aes256gcm/aesni Romain Dolbeau |
||||
|
Frank Denis |
||||
|
|
||||
|
crypto_aead/chacha20poly1305 Frank Denis |
||||
|
|
||||
|
crypto_aead/xchacha20poly1305 Frank Denis |
||||
|
Jason A. Donenfeld |
||||
|
|
||||
|
crypto_auth/hmacsha256 Colin Percival |
||||
|
crypto_auth/hmacsha512 |
||||
|
crypto_auth/hmacsha512256 |
||||
|
|
||||
|
crypto_box/curve25519xsalsa20poly1305 Daniel J. Bernstein |
||||
|
|
||||
|
crypto_box/curve25519xchacha20poly1305 Frank Denis |
||||
|
|
||||
|
crypto_core/ed25519 Daniel J. Bernstein |
||||
|
Adam Langley |
||||
|
|
||||
|
crypto_core/hchacha20 Frank Denis |
||||
|
|
||||
|
crypto_core/hsalsa20 Daniel J. Bernstein |
||||
|
crypto_core/salsa |
||||
|
|
||||
|
crypto_generichash/blake2b Jean-Philippe Aumasson |
||||
|
Christian Winnerlein |
||||
|
Samuel Neves |
||||
|
Zooko Wilcox-O'Hearn |
||||
|
|
||||
|
crypto_hash/sha256 Colin Percival |
||||
|
crypto_hash/sha512 |
||||
|
crypto_hash/sha512256 |
||||
|
|
||||
|
crypto_kdf Frank Denis |
||||
|
|
||||
|
crypto_kx Frank Denis |
||||
|
|
||||
|
crypto_onetimeauth/poly1305/donna Andrew "floodyberry" Moon |
||||
|
crypto_onetimeauth/poly1305/sse2 |
||||
|
|
||||
|
crypto_pwhash/argon2 Samuel Neves |
||||
|
Dmitry Khovratovich |
||||
|
Jean-Philippe Aumasson |
||||
|
Daniel Dinu |
||||
|
Thomas Pornin |
||||
|
|
||||
|
crypto_pwhash/scryptsalsa208sha256 Colin Percival |
||||
|
Alexander Peslyak |
||||
|
|
||||
|
crypto_scalarmult/curve25519/ref10 Daniel J. Bernstein |
||||
|
|
||||
|
crypto_scalarmult/curve25519/sandy2x Tung Chou |
||||
|
|
||||
|
crypto_scalarmult/ed25519 Frank Denis |
||||
|
|
||||
|
crypto_secretbox/xsalsa20poly1305 Daniel J. Bernstein |
||||
|
|
||||
|
crypto_secretbox/xchacha20poly1305 Frank Denis |
||||
|
|
||||
|
crypto_secretstream/xchacha20poly1305 Frank Denis |
||||
|
|
||||
|
crypto_shorthash/siphash24 Jean-Philippe Aumasson |
||||
|
Daniel J. Bernstein |
||||
|
|
||||
|
crypto_sign/ed25519 Peter Schwabe |
||||
|
Daniel J. Bernstein |
||||
|
Niels Duif |
||||
|
Tanja Lange |
||||
|
Bo-Yin Yang |
||||
|
|
||||
|
crypto_stream/chacha20/ref Daniel J. Bernstein |
||||
|
|
||||
|
crypto_stream/chacha20/dolbeau Romain Dolbeau |
||||
|
Daniel J. Bernstein |
||||
|
|
||||
|
crypto_stream/salsa20/ref Daniel J. Bernstein |
||||
|
crypto_stream/salsa20/xmm6 |
||||
|
|
||||
|
crypto_stream/salsa20/xmm6int Romain Dolbeau |
||||
|
Daniel J. Bernstein |
||||
|
|
||||
|
crypto_stream/salsa2012/ref Daniel J. Bernstein |
||||
|
crypto_stream/salsa2008/ref |
||||
|
|
||||
|
crypto_stream/xchacha20 Frank Denis |
||||
|
|
||||
|
crypto_verify Frank Denis |
||||
|
|
||||
|
sodium/codecs.c Frank Denis |
||||
|
Thomas Pornin |
||||
|
Christian Winnerlein |
||||
|
|
||||
|
sodium/core.c Frank Denis |
||||
|
sodium/runtime.h |
||||
|
sodium/utils.c |
@ -0,0 +1,505 @@ |
|||||
|
|
||||
|
* Version 1.0.16 |
||||
|
- Signatures computations and verifications are now way faster on |
||||
|
64-bit platforms with compilers supporting 128-bit arithmetic (gcc, |
||||
|
clang, icc). This includes the WebAssembly target. |
||||
|
- New low-level APIs for computations over edwards25519: |
||||
|
`crypto_scalarmult_ed25519()`, `crypto_scalarmult_ed25519_base()`, |
||||
|
`crypto_core_ed25519_is_valid_point()`, `crypto_core_ed25519_add()`, |
||||
|
`crypto_core_ed25519_sub()` and `crypto_core_ed25519_from_uniform()` |
||||
|
(elligator representative to point). |
||||
|
- `crypto_sign_open()`, `crypto_sign_verify_detached() and |
||||
|
`crypto_sign_edwards25519sha512batch_open` now reject public keys in |
||||
|
non-canonical form in addition to low-order points. |
||||
|
- The library can be built with `ED25519_NONDETERMINISTIC` defined in |
||||
|
order to use synthetic nonces for EdDSA. This is disabled by default. |
||||
|
- Webassembly: `crypto_pwhash_*()` functions are now included in |
||||
|
non-sumo builds. |
||||
|
- `sodium_stackzero()` was added to wipe content off the stack. |
||||
|
- Android: support new SDKs where unified headers have become the |
||||
|
default. |
||||
|
- The Salsa20-based PRNG example is now thread-safe on platforms with |
||||
|
support for thread-local storage, optionally mixes bits from RDRAND. |
||||
|
- CMAKE: static library detection on Unix systems has been improved |
||||
|
(thanks to @BurningEnlightenment, @nibua-r, @mellery451) |
||||
|
- Argon2 and scrypt are slightly faster on Linux. |
||||
|
|
||||
|
* Version 1.0.15 |
||||
|
- The default password hashing algorithm is now Argon2id. The |
||||
|
`pwhash_str_verify()` function can still verify Argon2i hashes |
||||
|
without any changes, and `pwhash()` can still compute Argon2i hashes |
||||
|
as well. |
||||
|
- The aes128ctr primitive was removed. It was slow, non-standard, not |
||||
|
authenticated, and didn't seem to be used by any opensource project. |
||||
|
- Argon2id required at least 3 passes like Argon2i, despite a minimum |
||||
|
of `1` as defined by the `OPSLIMIT_MIN` constant. This has been fixed. |
||||
|
- The secretstream construction was slightly changed to be consistent |
||||
|
with forthcoming variants. |
||||
|
- The Javascript and Webassembly versions have been merged, and the |
||||
|
module now returns a `.ready` promise that will resolve after the |
||||
|
Webassembly code is loaded and compiled. |
||||
|
- Note that due to these incompatible changes, the library version |
||||
|
major was bumped up. |
||||
|
|
||||
|
* Version 1.0.14 |
||||
|
- iOS binaries should now be compatible with WatchOS and TVOS. |
||||
|
- WebAssembly is now officially supported. Special thanks to |
||||
|
@facekapow and @pepyakin who helped to make it happen. |
||||
|
- Internal consistency checks failing and primitives used with |
||||
|
dangerous/out-of-bounds/invalid parameters used to call abort(3). |
||||
|
Now, a custom handler *that doesn't return* can be set with the |
||||
|
`set_sodium_misuse()` function. It still aborts by default or if the |
||||
|
handler ever returns. This is not a replacement for non-fatal, |
||||
|
expected runtime errors. This handler will be only called in |
||||
|
unexpected situations due to potential bugs in the library or in |
||||
|
language bindings. |
||||
|
- `*_MESSAGEBYTES_MAX` macros (and the corresponding |
||||
|
`_messagebytes_max()` symbols) have been added to represent the |
||||
|
maximum message size that can be safely handled by a primitive. |
||||
|
Language bindings are encouraged to check user inputs against these |
||||
|
maximum lengths. |
||||
|
- The test suite has been extended to cover more edge cases. |
||||
|
- crypto_sign_ed25519_pk_to_curve25519() now rejects points that are |
||||
|
not on the curve, or not in the main subgroup. |
||||
|
- Further changes have been made to ensure that smart compilers will |
||||
|
not optimize out code that we don't want to be optimized. |
||||
|
- Visual Studio solutions are now included in distribution tarballs. |
||||
|
- The `sodium_runtime_has_*` symbols for CPU features detection are |
||||
|
now defined as weak symbols, i.e. they can be replaced with an |
||||
|
application-defined implementation. This can be useful to disable |
||||
|
AVX* when temperature/power consumption is a concern. |
||||
|
- `crypto_kx_*()` now aborts if called with no non-NULL pointers to |
||||
|
store keys to. |
||||
|
- SSE2 implementations of `crypto_verify_*()` have been added. |
||||
|
- Passwords can be hashed using a specific algorithm with the new |
||||
|
`crypto_pwhash_str_alg()` function. |
||||
|
- Due to popular demand, base64 encoding (`sodium_bin2base64()`) and |
||||
|
decoding (`sodium_base642bin()`) have been implemented. |
||||
|
- A new `crypto_secretstream_*()` API was added to safely encrypt files |
||||
|
and multi-part messages. |
||||
|
- The `sodium_pad()` and `sodium_unpad()` helper functions have been |
||||
|
added in order to add & remove padding. |
||||
|
- An AVX512 optimized implementation of Argon2 has been added (written |
||||
|
by Ondrej Mosnáček, thanks!) |
||||
|
- The `crypto_pwhash_str_needs_rehash()` function was added to check if |
||||
|
a password hash string matches the given parameters, or if it needs an |
||||
|
update. |
||||
|
- The library can now be compiled with recent versions of |
||||
|
emscripten/binaryen that don't allow multiple variables declarations |
||||
|
using a single `var` statement. |
||||
|
|
||||
|
* Version 1.0.13 |
||||
|
- Javascript: the sumo builds now include all symbols. They were |
||||
|
previously limited to symbols defined in minimal builds. |
||||
|
- The public `crypto_pwhash_argon2i_MEMLIMIT_MAX` constant was |
||||
|
incorrectly defined on 32-bit platforms. This has been fixed. |
||||
|
- Version 1.0.12 didn't compile on OpenBSD/i386 using the base gcc |
||||
|
compiler. This has been fixed. |
||||
|
- The Android compilation scripts have been updated for NDK r14b. |
||||
|
- armv7s-optimized code was re-added to iOS builds. |
||||
|
- An AVX2 optimized implementation of the Argon2 round function was |
||||
|
added. |
||||
|
- The Argon2id variant of Argon2 has been implemented. The |
||||
|
high-level `crypto_pwhash_str_verify()` function automatically detects |
||||
|
the algorithm and can verify both Argon2i and Argon2id hashed passwords. |
||||
|
The default algorithm for newly hashed passwords remains Argon2i in |
||||
|
this version to avoid breaking compatibility with verifiers running |
||||
|
libsodium <= 1.0.12. |
||||
|
- A `crypto_box_curve25519xchacha20poly1305_seal*()` function set was |
||||
|
implemented. |
||||
|
- scrypt was removed from minimal builds. |
||||
|
- libsodium is now available on NuGet. |
||||
|
|
||||
|
* Version 1.0.12 |
||||
|
- Ed25519ph was implemented, adding a multi-part signature API |
||||
|
(`crypto_sign_init()`, `crypto_sign_update()`, `crypto_sign_final_*()`). |
||||
|
- New constants and related accessors have been added for Scrypt and |
||||
|
Argon2. |
||||
|
- XChaCha20 has been implemented. Like XSalsa20, this construction |
||||
|
extends the ChaCha20 cipher to accept a 192-bit nonce. This makes it safe |
||||
|
to use ChaCha20 with random nonces. |
||||
|
- `crypto_secretbox`, `crypto_box` and `crypto_aead` now offer |
||||
|
variants leveraging XChaCha20. |
||||
|
- SHA-2 is about 20% faster, which also gives a speed boost to |
||||
|
signature and signature verification. |
||||
|
- AVX2 implementations of Salsa20 and ChaCha20 have been added. They |
||||
|
are twice as fast as the SSE2 implementations. The speed gain is |
||||
|
even more significant on Windows, that previously didn't use |
||||
|
vectorized implementations. |
||||
|
- New high-level API: `crypto_kdf`, to easily derive one or more |
||||
|
subkeys from a master key. |
||||
|
- Siphash with a 128-bit output has been implemented, and is |
||||
|
available as `crypto_shorthash_siphashx_*`. |
||||
|
- New `*_keygen()` helpers functions have been added to create secret |
||||
|
keys for all constructions. This improves code clarity and can prevent keys |
||||
|
from being partially initialized. |
||||
|
- A new `randombytes_buf_deterministic()` function was added to |
||||
|
deterministically fill a memory region with pseudorandom data. This |
||||
|
function can especially be useful to write reproducible tests. |
||||
|
- A preliminary `crypto_kx_*()` API was added to compute shared session |
||||
|
keys. |
||||
|
- AVX2 detection is more reliable. |
||||
|
- The pthreads library is not required any more when using MingW. |
||||
|
- `contrib/Findsodium.cmake` was added as an example to include |
||||
|
libsodium in a project using cmake. |
||||
|
- Compatibility with gcc 2.x has been restored. |
||||
|
- Minimal builds can be checked using `sodium_library_minimal()`. |
||||
|
- The `--enable-opt` compilation switch has become compatible with more |
||||
|
platforms. |
||||
|
- Android builds are now using clang on platforms where it is |
||||
|
available. |
||||
|
|
||||
|
* Version 1.0.11 |
||||
|
- `sodium_init()` is now thread-safe, and can be safely called multiple |
||||
|
times. |
||||
|
- Android binaries now properly support 64-bit Android, targeting |
||||
|
platform 24, but without breaking compatibility with platforms 16 and |
||||
|
21. |
||||
|
- Better support for old gcc versions. |
||||
|
- On FreeBSD, core dumps are disabled on regions allocated with |
||||
|
sodium allocation functions. |
||||
|
- AVX2 detection was fixed, resulting in faster Blake2b hashing on |
||||
|
platforms where it was not properly detected. |
||||
|
- The Sandy2x Curve25519 implementation was not as fast as expected |
||||
|
on some platforms. This has been fixed. |
||||
|
- The NativeClient target was improved. Most notably, it now supports |
||||
|
optimized implementations, and uses pepper_49 by default. |
||||
|
- The library can be compiled with recent Emscripten versions. |
||||
|
Changes have been made to produce smaller code, and the default heap |
||||
|
size was reduced in the standard version. |
||||
|
- The code can now be compiled on SLES11 service pack 4. |
||||
|
- Decryption functions can now accept a NULL pointer for the output. |
||||
|
This checks the MAC without writing the decrypted message. |
||||
|
- crypto_generichash_final() now returns -1 if called twice. |
||||
|
- Support for Visual Studio 2008 was improved. |
||||
|
|
||||
|
* Version 1.0.10 |
||||
|
- This release only fixes a compilation issue reported with some older |
||||
|
gcc versions. There are no functional changes over the previous release. |
||||
|
|
||||
|
* Version 1.0.9 |
||||
|
- The Javascript target now includes a `--sumo` option to include all |
||||
|
the symbols of the original C library. |
||||
|
- A detached API was added to the ChaCha20-Poly1305 and AES256-GCM |
||||
|
implementations. |
||||
|
- The Argon2i password hashing function was added, and is accessible |
||||
|
directly and through a new, high-level `crypto_pwhash` API. The scrypt |
||||
|
function remains available as well. |
||||
|
- A speed-record AVX2 implementation of BLAKE2b was added (thanks to |
||||
|
Samuel Neves). |
||||
|
- The library can now be compiled using C++Builder (thanks to @jcolli44) |
||||
|
- Countermeasures for Ed25519 signatures malleability have been added |
||||
|
to match the irtf-cfrg-eddsa draft (note that malleability is irrelevant to |
||||
|
the standard definition of signature security). Signatures with a small-order |
||||
|
`R` point are now also rejected. |
||||
|
- Some implementations are now slightly faster when using the Clang |
||||
|
compiler. |
||||
|
- The HChaCha20 core function was implemented (`crypto_core_hchacha20()`). |
||||
|
- No-op stubs were added for all AES256-GCM public functions even when |
||||
|
compiled on non-Intel platforms. |
||||
|
- `crypt_generichash_blake2b_statebytes()` was added. |
||||
|
- New macros were added for the IETF variant of the ChaCha20-Poly1305 |
||||
|
construction. |
||||
|
- The library can now be compiled on Minix. |
||||
|
- HEASLR is now enabled on MinGW builds. |
||||
|
|
||||
|
* Version 1.0.8 |
||||
|
- Handle the case where the CPU supports AVX, but we are running |
||||
|
on an hypervisor with AVX disabled/not supported. |
||||
|
- Faster (2x) scalarmult_base() when using the ref10 implementation. |
||||
|
|
||||
|
* Version 1.0.7 |
||||
|
- More functions whose return value should be checked have been |
||||
|
tagged with `__attribute__ ((warn_unused_result))`: `crypto_box_easy()`, |
||||
|
`crypto_box_detached()`, `crypto_box_beforenm()`, `crypto_box()`, and |
||||
|
`crypto_scalarmult()`. |
||||
|
- Sandy2x, the fastest Curve25519 implementation ever, has been |
||||
|
merged in, and is automatically used on CPUs supporting the AVX |
||||
|
instructions set. |
||||
|
- An SSE2 optimized implementation of Poly1305 was added, and is |
||||
|
twice as fast as the portable one. |
||||
|
- An SSSE3 optimized implementation of ChaCha20 was added, and is |
||||
|
twice as fast as the portable one. |
||||
|
- Faster `sodium_increment()` for common nonce sizes. |
||||
|
- New helper functions have been added: `sodium_is_zero()` and |
||||
|
`sodium_add()`. |
||||
|
- `sodium_runtime_has_aesni()` now properly detects the CPU flag when |
||||
|
compiled using Visual Studio. |
||||
|
|
||||
|
* Version 1.0.6 |
||||
|
- Optimized implementations of Blake2 have been added for modern |
||||
|
Intel platforms. `crypto_generichash()` is now faster than MD5 and SHA1 |
||||
|
implementations while being far more secure. |
||||
|
- Functions for which the return value should be checked have been |
||||
|
tagged with `__attribute__ ((warn_unused_result))`. This will |
||||
|
intentionally break code compiled with `-Werror` that didn't bother |
||||
|
checking critical return values. |
||||
|
- The `crypto_sign_edwards25519sha512batch_*()` functions have been |
||||
|
tagged as deprecated. |
||||
|
- Undocumented symbols that were exported, but were only useful for |
||||
|
internal purposes have been removed or made private: |
||||
|
`sodium_runtime_get_cpu_features()`, the implementation-specific |
||||
|
`crypto_onetimeauth_poly1305_donna()` symbols, |
||||
|
`crypto_onetimeauth_poly1305_set_implementation()`, |
||||
|
`crypto_onetimeauth_poly1305_implementation_name()` and |
||||
|
`crypto_onetimeauth_pick_best_implementation()`. |
||||
|
- `sodium_compare()` now works as documented, and compares numbers |
||||
|
in little-endian format instead of behaving like `memcmp()`. |
||||
|
- The previous changes should not break actual applications, but to be |
||||
|
safe, the library version major was incremented. |
||||
|
- `sodium_runtime_has_ssse3()` and `sodium_runtime_has_sse41()` have |
||||
|
been added. |
||||
|
- The library can now be compiled with the CompCert compiler. |
||||
|
|
||||
|
* Version 1.0.5 |
||||
|
- Compilation issues on some platforms were fixed: missing alignment |
||||
|
directives were added (required at least on RHEL-6/i386), a workaround |
||||
|
for a VRP bug on gcc/armv7 was added, and the library can now be compiled |
||||
|
with the SunPro compiler. |
||||
|
- Javascript target: io.js is not supported any more. Use nodejs. |
||||
|
|
||||
|
* Version 1.0.4 |
||||
|
- Support for AES256-GCM has been added. This requires |
||||
|
a CPU with the aesni and pclmul extensions, and is accessible via the |
||||
|
crypto_aead_aes256gcm_*() functions. |
||||
|
- The Javascript target doesn't use eval() any more, so that the |
||||
|
library can be used in Chrome packaged applications. |
||||
|
- QNX and CloudABI are now supported. |
||||
|
- Support for NaCl has finally been added. |
||||
|
- ChaCha20 with an extended (96 bit) nonce and a 32-bit counter has |
||||
|
been implemented as crypto_stream_chacha20_ietf(), |
||||
|
crypto_stream_chacha20_ietf_xor() and crypto_stream_chacha20_ietf_xor_ic(). |
||||
|
An IETF-compatible version of ChaCha20Poly1305 is available as |
||||
|
crypto_aead_chacha20poly1305_ietf_npubbytes(), |
||||
|
crypto_aead_chacha20poly1305_ietf_encrypt() and |
||||
|
crypto_aead_chacha20poly1305_ietf_decrypt(). |
||||
|
- The sodium_increment() helper function has been added, to increment |
||||
|
an arbitrary large number (such as a nonce). |
||||
|
- The sodium_compare() helper function has been added, to compare |
||||
|
arbitrary large numbers (such as nonces, in order to prevent replay |
||||
|
attacks). |
||||
|
|
||||
|
* Version 1.0.3 |
||||
|
- In addition to sodium_bin2hex(), sodium_hex2bin() is now a |
||||
|
constant-time function. |
||||
|
- crypto_stream_xsalsa20_ic() has been added. |
||||
|
- crypto_generichash_statebytes(), crypto_auth_*_statebytes() and |
||||
|
crypto_hash_*_statebytes() have been added in order to retrieve the |
||||
|
size of structures keeping states from foreign languages. |
||||
|
- The JavaScript target doesn't require /dev/urandom or an external |
||||
|
randombytes() implementation any more. Other minor Emscripten-related |
||||
|
improvements have been made in order to support libsodium.js |
||||
|
- Custom randombytes implementations do not need to provide their own |
||||
|
implementation of randombytes_uniform() any more. randombytes_stir() |
||||
|
and randombytes_close() can also be NULL pointers if they are not |
||||
|
required. |
||||
|
- On Linux, getrandom(2) is being used instead of directly accessing |
||||
|
/dev/urandom, if the kernel supports this system call. |
||||
|
- crypto_box_seal() and crypto_box_seal_open() have been added. |
||||
|
- Visual Studio 2015 is now supported. |
||||
|
|
||||
|
* Version 1.0.2 |
||||
|
- The _easy and _detached APIs now support precalculated keys; |
||||
|
crypto_box_easy_afternm(), crypto_box_open_easy_afternm(), |
||||
|
crypto_box_detached_afternm() and crypto_box_open_detached_afternm() |
||||
|
have been added as an alternative to the NaCl interface. |
||||
|
- Memory allocation functions can now be used on operating systems with |
||||
|
no memory protection. |
||||
|
- crypto_sign_open() and crypto_sign_edwards25519sha512batch_open() |
||||
|
now accept a NULL pointer instead of a pointer to the message size, if |
||||
|
storing this information is not required. |
||||
|
- The close-on-exec flag is now set on the descriptor returned when |
||||
|
opening /dev/urandom. |
||||
|
- A libsodium-uninstalled.pc file to use pkg-config even when |
||||
|
libsodium is not installed, has been added. |
||||
|
- The iOS target now includes armv7s and arm64 optimized code, as well |
||||
|
as i386 and x86_64 code for the iOS simulator. |
||||
|
- sodium_free() can now be called on regions with PROT_NONE protection. |
||||
|
- The Javascript tests can run on Ubuntu, where the node binary was |
||||
|
renamed nodejs. io.js can also be used instead of node. |
||||
|
|
||||
|
* Version 1.0.1 |
||||
|
- DLL_EXPORT was renamed SODIUM_DLL_EXPORT in order to avoid |
||||
|
collisions with similar macros defined by other libraries. |
||||
|
- sodium_bin2hex() is now constant-time. |
||||
|
- crypto_secretbox_detached() now supports overlapping input and output |
||||
|
regions. |
||||
|
- NaCl's donna_c64 implementation of curve25519 was reading an extra byte |
||||
|
past the end of the buffer containing the base point. This has been |
||||
|
fixed. |
||||
|
|
||||
|
* Version 1.0.0 |
||||
|
- The API and ABI are now stable. New features will be added, but |
||||
|
backward-compatibility is guaranteed through all the 1.x.y releases. |
||||
|
- crypto_sign() properly works with overlapping regions again. Thanks |
||||
|
to @pysiak for reporting this regression introduced in version 0.6.1. |
||||
|
- The test suite has been extended. |
||||
|
|
||||
|
* Version 0.7.1 (1.0 RC2) |
||||
|
- This is the second release candidate of Sodium 1.0. Minor |
||||
|
compilation, readability and portability changes have been made and the |
||||
|
test suite was improved, but the API is the same as the previous release |
||||
|
candidate. |
||||
|
|
||||
|
* Version 0.7.0 (1.0 RC1) |
||||
|
- Allocating memory to store sensitive data can now be done using |
||||
|
sodium_malloc() and sodium_allocarray(). These functions add guard |
||||
|
pages around the protected data to make it less likely to be |
||||
|
accessible in a heartbleed-like scenario. In addition, the protection |
||||
|
for memory regions allocated that way can be changed using |
||||
|
sodium_mprotect_noaccess(), sodium_mprotect_readonly() and |
||||
|
sodium_mprotect_readwrite(). |
||||
|
- ed25519 keys can be converted to curve25519 keys with |
||||
|
crypto_sign_ed25519_pk_to_curve25519() and |
||||
|
crypto_sign_ed25519_sk_to_curve25519(). This allows using the same |
||||
|
keys for signature and encryption. |
||||
|
- The seed and the public key can be extracted from an ed25519 key |
||||
|
using crypto_sign_ed25519_sk_to_seed() and crypto_sign_ed25519_sk_to_pk(). |
||||
|
- aes256 was removed. A timing-attack resistant implementation might |
||||
|
be added later, but not before version 1.0 is tagged. |
||||
|
- The crypto_pwhash_scryptxsalsa208sha256_* compatibility layer was |
||||
|
removed. Use crypto_pwhash_scryptsalsa208sha256_*. |
||||
|
- The compatibility layer for implementation-specific functions was |
||||
|
removed. |
||||
|
- Compilation issues with Mingw64 on MSYS (not MSYS2) were fixed. |
||||
|
- crypto_pwhash_scryptsalsa208sha256_STRPREFIX was added: it contains |
||||
|
the prefix produced by crypto_pwhash_scryptsalsa208sha256_str() |
||||
|
|
||||
|
* Version 0.6.1 |
||||
|
- Important bug fix: when crypto_sign_open() was given a signed |
||||
|
message too short to even contain a signature, it was putting an |
||||
|
unlimited amount of zeros into the target buffer instead of |
||||
|
immediately returning -1. The bug was introduced in version 0.5.0. |
||||
|
- New API: crypto_sign_detached() and crypto_sign_verify_detached() |
||||
|
to produce and verify ed25519 signatures without having to duplicate |
||||
|
the message. |
||||
|
- New ./configure switch: --enable-minimal, to create a smaller |
||||
|
library, with only the functions required for the high-level API. |
||||
|
Mainly useful for the JavaScript target and embedded systems. |
||||
|
- All the symbols are now exported by the Emscripten build script. |
||||
|
- The pkg-config .pc file is now always installed even if the |
||||
|
pkg-config tool is not available during the installation. |
||||
|
|
||||
|
* Version 0.6.0 |
||||
|
- The ChaCha20 stream cipher has been added, as crypto_stream_chacha20_* |
||||
|
- The ChaCha20Poly1305 AEAD construction has been implemented, as |
||||
|
crypto_aead_chacha20poly1305_* |
||||
|
- The _easy API does not require any heap allocations any more and |
||||
|
does not have any overhead over the NaCl API. With the password |
||||
|
hashing function being an obvious exception, the library doesn't |
||||
|
allocate and will not allocate heap memory ever. |
||||
|
- crypto_box and crypto_secretbox have a new _detached API to store |
||||
|
the authentication tag and the encrypted message separately. |
||||
|
- crypto_pwhash_scryptxsalsa208sha256*() functions have been renamed |
||||
|
crypto_pwhash_scryptsalsa208sha256*(). |
||||
|
- The low-level crypto_pwhash_scryptsalsa208sha256_ll() function |
||||
|
allows setting individual parameters of the scrypt function. |
||||
|
- New macros and functions for recommended crypto_pwhash_* parameters |
||||
|
have been added. |
||||
|
- Similarly to crypto_sign_seed_keypair(), crypto_box_seed_keypair() |
||||
|
has been introduced to deterministically generate a key pair from a seed. |
||||
|
- crypto_onetimeauth() now provides a streaming interface. |
||||
|
- crypto_stream_chacha20_xor_ic() and crypto_stream_salsa20_xor_ic() |
||||
|
have been added to use a non-zero initial block counter. |
||||
|
- On Windows, CryptGenRandom() was replaced by RtlGenRandom(), which |
||||
|
doesn't require the Crypt API. |
||||
|
- The high bit in curve25519 is masked instead of processing the key as |
||||
|
a 256-bit value. |
||||
|
- The curve25519 ref implementation was replaced by the latest ref10 |
||||
|
implementation from Supercop. |
||||
|
- sodium_mlock() now prevents memory from being included in coredumps |
||||
|
on Linux 3.4+ |
||||
|
|
||||
|
* Version 0.5.0 |
||||
|
- sodium_mlock()/sodium_munlock() have been introduced to lock pages |
||||
|
in memory before storing sensitive data, and to zero them before |
||||
|
unlocking them. |
||||
|
- High-level wrappers for crypto_box and crypto_secretbox |
||||
|
(crypto_box_easy and crypto_secretbox_easy) can be used to avoid |
||||
|
dealing with the specific memory layout regular functions depend on. |
||||
|
- crypto_pwhash_scryptsalsa208sha256* functions have been added |
||||
|
to derive a key from a password, and for password storage. |
||||
|
- Salsa20 and ed25519 implementations now support overlapping |
||||
|
inputs/keys/outputs (changes imported from supercop-20140505). |
||||
|
- New build scripts for Visual Studio, Emscripten, different Android |
||||
|
architectures and msys2 are available. |
||||
|
- The poly1305-53 implementation has been replaced with Floodyberry's |
||||
|
poly1305-donna32 and poly1305-donna64 implementations. |
||||
|
- sodium_hex2bin() has been added to complement sodium_bin2hex(). |
||||
|
- On OpenBSD and Bitrig, arc4random() is used instead of reading |
||||
|
/dev/urandom. |
||||
|
- crypto_auth_hmac_sha512() has been implemented. |
||||
|
- sha256 and sha512 now have a streaming interface. |
||||
|
- hmacsha256, hmacsha512 and hmacsha512256 now support keys of |
||||
|
arbitrary length, and have a streaming interface. |
||||
|
- crypto_verify_64() has been implemented. |
||||
|
- first-class Visual Studio build system, thanks to @evoskuil |
||||
|
- CPU features are now detected at runtime. |
||||
|
|
||||
|
* Version 0.4.5 |
||||
|
- Restore compatibility with OSX <= 10.6 |
||||
|
|
||||
|
* Version 0.4.4 |
||||
|
- Visual Studio is officially supported (VC 2010 & VC 2013) |
||||
|
- mingw64 is now supported |
||||
|
- big-endian architectures are now supported as well |
||||
|
- The donna_c64 implementation of curve25519_donna_c64 now handles |
||||
|
non-canonical points like the ref implementation |
||||
|
- Missing scalarmult_curve25519 and stream_salsa20 constants are now exported |
||||
|
- A crypto_onetimeauth_poly1305_ref() wrapper has been added |
||||
|
|
||||
|
* Version 0.4.3 |
||||
|
- crypto_sign_seedbytes() and crypto_sign_SEEDBYTES were added. |
||||
|
- crypto_onetimeauth_poly1305_implementation_name() was added. |
||||
|
- poly1305-ref has been replaced by a faster implementation, |
||||
|
Floodyberry's poly1305-donna-unrolled. |
||||
|
- Stackmarkings have been added to assembly code, for Hardened Gentoo. |
||||
|
- pkg-config can now be used in order to retrieve compilations flags for |
||||
|
using libsodium. |
||||
|
- crypto_stream_aes256estream_*() can now deal with unaligned input |
||||
|
on platforms that require word alignment. |
||||
|
- portability improvements. |
||||
|
|
||||
|
* Version 0.4.2 |
||||
|
- All NaCl constants are now also exposed as functions. |
||||
|
- The Android and iOS cross-compilation script have been improved. |
||||
|
- libsodium can now be cross-compiled to Windows from Linux. |
||||
|
- libsodium can now be compiled with emscripten. |
||||
|
- New convenience function (prototyped in utils.h): sodium_bin2hex(). |
||||
|
|
||||
|
* Version 0.4.1 |
||||
|
- sodium_version_*() functions were not exported in version 0.4. They |
||||
|
are now visible as intended. |
||||
|
- sodium_init() now calls randombytes_stir(). |
||||
|
- optimized assembly version of salsa20 is now used on amd64. |
||||
|
- further cleanups and enhanced compatibility with non-C99 compilers. |
||||
|
|
||||
|
* Version 0.4 |
||||
|
- Most constants and operations are now available as actual functions |
||||
|
instead of macros, making it easier to use from other languages. |
||||
|
- New operation: crypto_generichash, featuring a variable key size, a |
||||
|
variable output size, and a streaming API. Currently implemented using |
||||
|
Blake2b. |
||||
|
- The package can be compiled in a separate directory. |
||||
|
- aes128ctr functions are exported. |
||||
|
- Optimized versions of curve25519 (curve25519_donna_c64), poly1305 |
||||
|
(poly1305_53) and ed25519 (ed25519_ref10) are available. Optionally calling |
||||
|
sodium_init() once before using the library makes it pick the fastest |
||||
|
implementation. |
||||
|
- New convenience function: sodium_memzero() in order to securely |
||||
|
wipe a memory area. |
||||
|
- A whole bunch of cleanups and portability enhancements. |
||||
|
- On Windows, a .REF file is generated along with the shared library, |
||||
|
for use with Visual Studio. The installation path for these has become |
||||
|
$prefix/bin as expected by MingW. |
||||
|
|
||||
|
* Version 0.3 |
||||
|
- The crypto_shorthash operation has been added, implemented using |
||||
|
SipHash-2-4. |
||||
|
|
||||
|
* Version 0.2 |
||||
|
- crypto_sign_seed_keypair() has been added |
||||
|
|
||||
|
* Version 0.1 |
||||
|
- Initial release. |
||||
|
|
@ -0,0 +1,18 @@ |
|||||
|
/* |
||||
|
* ISC License |
||||
|
* |
||||
|
* Copyright (c) 2013-2017 |
||||
|
* Frank Denis <j at pureftpd dot org> |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
*/ |
@ -0,0 +1,24 @@ |
|||||
|
ACLOCAL_AMFLAGS = -I m4 |
||||
|
|
||||
|
EXTRA_DIST = \
|
||||
|
autogen.sh \
|
||||
|
libsodium.sln \
|
||||
|
libsodium.vcxproj \
|
||||
|
libsodium.vcxproj.filters \
|
||||
|
LICENSE \
|
||||
|
README.markdown \
|
||||
|
THANKS |
||||
|
|
||||
|
SUBDIRS = \
|
||||
|
builds \
|
||||
|
contrib \
|
||||
|
dist-build \
|
||||
|
msvc-scripts \
|
||||
|
src \
|
||||
|
test |
||||
|
|
||||
|
pkgconfigdir = $(libdir)/pkgconfig |
||||
|
pkgconfig_DATA = @PACKAGE_NAME@.pc |
||||
|
|
||||
|
DISTCLEANFILES = $(pkgconfig_DATA) |
||||
|
|
@ -0,0 +1,46 @@ |
|||||
|
[![Build Status](https://travis-ci.org/jedisct1/libsodium.svg?branch=master)](https://travis-ci.org/jedisct1/libsodium?branch=master) |
||||
|
[![Windows build status](https://ci.appveyor.com/api/projects/status/fu8s2elx25il98hj?svg=true)](https://ci.appveyor.com/project/jedisct1/libsodium) |
||||
|
[![Coverity Scan Build Status](https://scan.coverity.com/projects/2397/badge.svg)](https://scan.coverity.com/projects/2397) |
||||
|
|
||||
|
![libsodium](https://raw.github.com/jedisct1/libsodium/master/logo.png) |
||||
|
============ |
||||
|
|
||||
|
Sodium is a new, easy-to-use software library for encryption, |
||||
|
decryption, signatures, password hashing and more. |
||||
|
|
||||
|
It is a portable, cross-compilable, installable, packageable |
||||
|
fork of [NaCl](http://nacl.cr.yp.to/), with a compatible API, and an |
||||
|
extended API to improve usability even further. |
||||
|
|
||||
|
Its goal is to provide all of the core operations needed to build |
||||
|
higher-level cryptographic tools. |
||||
|
|
||||
|
Sodium supports a variety of compilers and operating systems, |
||||
|
including Windows (with MingW or Visual Studio, x86 and x64), iOS, Android, |
||||
|
as well as Javascript and Webassembly. |
||||
|
|
||||
|
## Documentation |
||||
|
|
||||
|
The documentation is available on Gitbook: |
||||
|
|
||||
|
* [libsodium documentation](https://download.libsodium.org/doc/) - |
||||
|
online, requires Javascript. |
||||
|
* [offline documentation](https://www.gitbook.com/book/jedisct1/libsodium/details) |
||||
|
in PDF, MOBI and ePUB formats. |
||||
|
|
||||
|
## Integrity Checking |
||||
|
|
||||
|
The integrity checking instructions (including the signing key for libsodium) |
||||
|
are available in the [installation](https://download.libsodium.org/doc/installation/index.html#integrity-checking) |
||||
|
section of the documentation. |
||||
|
|
||||
|
## Community |
||||
|
|
||||
|
A mailing-list is available to discuss libsodium. |
||||
|
|
||||
|
In order to join, just send a random mail to `sodium-subscribe` {at} |
||||
|
`pureftpd` {dot} `org`. |
||||
|
|
||||
|
## License |
||||
|
|
||||
|
[ISC license](https://en.wikipedia.org/wiki/ISC_license). |
@ -0,0 +1,91 @@ |
|||||
|
Special thanks to people, companies and organizations having written |
||||
|
libsodium bindings for their favorite programming languages: |
||||
|
|
||||
|
@alethia7 |
||||
|
@artemisc |
||||
|
@carblue |
||||
|
@dnaq |
||||
|
@ektrah |
||||
|
@graxrabble |
||||
|
@harleqin |
||||
|
@joshjdevl |
||||
|
@jrmarino |
||||
|
@jshahbazi |
||||
|
@lvh |
||||
|
@neheb |
||||
|
|
||||
|
Adam Caudill (@adamcaudill) |
||||
|
Alexander Morris (@alexpmorris) |
||||
|
Amit Murthy (@amitmurthy) |
||||
|
Andrew Bennett (@potatosalad) |
||||
|
Andrew Lambert (@charonn0) |
||||
|
Bruce Mitchener (@waywardmonkeys) |
||||
|
Bruno Oliveira (@abstractj) |
||||
|
Caolan McMahon (@caolan) |
||||
|
Chris Rebert (@cvrebert) |
||||
|
Christian Hermann (@bitbeans) |
||||
|
Christian Wiese (@morfoh) |
||||
|
Christian Wiese (@morfoh) |
||||
|
Colm MacCárthaigh (@colmmacc) |
||||
|
David Parrish (@dmp1ce) |
||||
|
Donald Stufft (@dstufft) |
||||
|
Douglas Campos (@qmx) |
||||
|
Drew Crawford (@drewcrawford) |
||||
|
Emil Bay (@emilbayes) |
||||
|
Eric Dong (@quantum1423) |
||||
|
Eric Voskuil (@evoskuil) |
||||
|
Farid Hajji (@fhajji) |
||||
|
Frank Siebenlist (@franks42) |
||||
|
Gabriel Handford (@gabriel) |
||||
|
Geo Carncross (@geocar) |
||||
|
Henrik Gassmann (BurningEnlightenment) |
||||
|
Jachym Holecek (@freza) |
||||
|
Jack Wink (@jackwink) |
||||
|
James Ruan (@jamesruan) |
||||
|
Jan de Muijnck-Hughes (@jfdm) |
||||
|
Jason McCampbell (@jasonmccampbell) |
||||
|
Jeroen Habraken (@VeXocide) |
||||
|
Jeroen Ooms (@jeroen) |
||||
|
Jesper Louis Andersen (@jlouis) |
||||
|
Joe Eli McIlvain (@jemc) |
||||
|
Jonathan Stowe (@jonathanstowe) |
||||
|
Joseph Abrahamson (@tel) |
||||
|
Julien Kauffmann (@ereOn) |
||||
|
Kenneth Ballenegger (@kballenegger) |
||||
|
Loic Maury (@loicmaury) |
||||
|
Michael Gorlick (@mgorlick) |
||||
|
Michael Gregorowicz (@mgregoro) |
||||
|
Michał Zieliński (@zielmicha) |
||||
|
Omar Ayub (@electricFeel) |
||||
|
Pedro Paixao (@paixaop) |
||||
|
Project ArteMisc (@artemisc) |
||||
|
Rich FitzJohn (@richfitz) |
||||
|
Ruben De Visscher (@rubendv) |
||||
|
Rudolf Von Krugstein (@rudolfvonkrugstein) |
||||
|
Samuel Neves (@sneves) |
||||
|
Scott Arciszewski (@paragonie-scott) |
||||
|
Stanislav Ovsiannikov (@naphaso) |
||||
|
Stefan Marsiske (@stef) |
||||
|
Stephan Touset (@stouset) |
||||
|
Stephen Chavez (@redragonx) |
||||
|
Steve Gibson (@sggrc) |
||||
|
Tony Arcieri (@bascule) |
||||
|
Tony Garnock-Jones (@tonyg) |
||||
|
Y. T. Chung (@zonyitoo) |
||||
|
|
||||
|
Bytecurry Software |
||||
|
Cryptotronix |
||||
|
Facebook |
||||
|
FSF France |
||||
|
MaidSafe |
||||
|
Paragonie Initiative Enterprises |
||||
|
Python Cryptographic Authority |
||||
|
|
||||
|
(this list may not be complete, if you don't see your name, please |
||||
|
submit a pull request!) |
||||
|
|
||||
|
Also thanks to: |
||||
|
|
||||
|
- Coverity, Inc. to provide static analysis. |
||||
|
- FSF France for providing access to their compilation servers. |
||||
|
- Private Internet Access for having sponsored a complete security audit. |
@ -0,0 +1,25 @@ |
|||||
|
version: 1.0.10.{build} |
||||
|
|
||||
|
os: Visual Studio 2015 |
||||
|
|
||||
|
environment: |
||||
|
matrix: |
||||
|
- platform: Win32 |
||||
|
configuration: Debug |
||||
|
- platform: Win32 |
||||
|
configuration: Release |
||||
|
- platform: x64 |
||||
|
configuration: Debug |
||||
|
- platform: x64 |
||||
|
configuration: Release |
||||
|
|
||||
|
matrix: |
||||
|
fast_finish: false |
||||
|
|
||||
|
init: |
||||
|
msbuild /version |
||||
|
|
||||
|
build: |
||||
|
parallel: true |
||||
|
project: libsodium.vcxproj |
||||
|
verbosity: minimal |
@ -0,0 +1,36 @@ |
|||||
|
#! /bin/sh |
||||
|
|
||||
|
if glibtoolize --version > /dev/null 2>&1; then |
||||
|
LIBTOOLIZE='glibtoolize' |
||||
|
else |
||||
|
LIBTOOLIZE='libtoolize' |
||||
|
fi |
||||
|
|
||||
|
command -v command >/dev/null 2>&1 || { |
||||
|
echo "command is required, but wasn't found on this system" |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
command -v $LIBTOOLIZE >/dev/null 2>&1 || { |
||||
|
echo "libtool is required, but wasn't found on this system" |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
command -v autoconf >/dev/null 2>&1 || { |
||||
|
echo "autoconf is required, but wasn't found on this system" |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
command -v automake >/dev/null 2>&1 || { |
||||
|
echo "automake is required, but wasn't found on this system" |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
if autoreconf --version > /dev/null 2>&1 ; then |
||||
|
exec autoreconf -ivf |
||||
|
fi |
||||
|
|
||||
|
$LIBTOOLIZE && \ |
||||
|
aclocal && \ |
||||
|
automake --add-missing --force-missing --include-deps && \ |
||||
|
autoconf |
@ -0,0 +1,7 @@ |
|||||
|
*.opensdf |
||||
|
*.suo |
||||
|
*.sdf |
||||
|
*.vcxproj.user |
||||
|
*.aps |
||||
|
*.log |
||||
|
!build |
@ -0,0 +1,65 @@ |
|||||
|
EXTRA_DIST = \
|
||||
|
msvc/build/buildall.bat \
|
||||
|
msvc/build/buildbase.bat \
|
||||
|
msvc/properties/Common.props \
|
||||
|
msvc/properties/Debug.props \
|
||||
|
msvc/properties/DebugDEXE.props \
|
||||
|
msvc/properties/DebugDLL.props \
|
||||
|
msvc/properties/DebugLEXE.props \
|
||||
|
msvc/properties/DebugLIB.props \
|
||||
|
msvc/properties/DebugLTCG.props \
|
||||
|
msvc/properties/DebugSEXE.props \
|
||||
|
msvc/properties/DLL.props \
|
||||
|
msvc/properties/EXE.props \
|
||||
|
msvc/properties/LIB.props \
|
||||
|
msvc/properties/Link.props \
|
||||
|
msvc/properties/LTCG.props \
|
||||
|
msvc/properties/Messages.props \
|
||||
|
msvc/properties/Output.props \
|
||||
|
msvc/properties/Release.props \
|
||||
|
msvc/properties/ReleaseDEXE.props \
|
||||
|
msvc/properties/ReleaseDLL.props \
|
||||
|
msvc/properties/ReleaseLEXE.props \
|
||||
|
msvc/properties/ReleaseLIB.props \
|
||||
|
msvc/properties/ReleaseLTCG.props \
|
||||
|
msvc/properties/ReleaseSEXE.props \
|
||||
|
msvc/properties/Win32.props \
|
||||
|
msvc/properties/x64.props \
|
||||
|
msvc/resource.h \
|
||||
|
msvc/resource.rc \
|
||||
|
msvc/version.h \
|
||||
|
msvc/vs2010/libsodium/libsodium.props \
|
||||
|
msvc/vs2010/libsodium/libsodium.vcxproj \
|
||||
|
msvc/vs2010/libsodium/libsodium.vcxproj.filters \
|
||||
|
msvc/vs2010/libsodium/libsodium.xml \
|
||||
|
msvc/vs2010/libsodium.import.props \
|
||||
|
msvc/vs2010/libsodium.import.xml \
|
||||
|
msvc/vs2010/libsodium.sln \
|
||||
|
msvc/vs2012/libsodium/libsodium.props \
|
||||
|
msvc/vs2012/libsodium/libsodium.vcxproj \
|
||||
|
msvc/vs2012/libsodium/libsodium.vcxproj.filters \
|
||||
|
msvc/vs2012/libsodium/libsodium.xml \
|
||||
|
msvc/vs2012/libsodium.import.props \
|
||||
|
msvc/vs2012/libsodium.import.xml \
|
||||
|
msvc/vs2012/libsodium.sln \
|
||||
|
msvc/vs2013/libsodium/libsodium.props \
|
||||
|
msvc/vs2013/libsodium/libsodium.vcxproj \
|
||||
|
msvc/vs2013/libsodium/libsodium.vcxproj.filters \
|
||||
|
msvc/vs2013/libsodium/libsodium.xml \
|
||||
|
msvc/vs2013/libsodium.import.props \
|
||||
|
msvc/vs2013/libsodium.import.xml \
|
||||
|
msvc/vs2013/libsodium.sln \
|
||||
|
msvc/vs2015/libsodium/libsodium.props \
|
||||
|
msvc/vs2015/libsodium/libsodium.vcxproj \
|
||||
|
msvc/vs2015/libsodium/libsodium.vcxproj.filters \
|
||||
|
msvc/vs2015/libsodium/libsodium.xml \
|
||||
|
msvc/vs2015/libsodium.import.props \
|
||||
|
msvc/vs2015/libsodium.import.xml \
|
||||
|
msvc/vs2015/libsodium.sln \
|
||||
|
msvc/vs2017/libsodium/libsodium.props \
|
||||
|
msvc/vs2017/libsodium/libsodium.vcxproj \
|
||||
|
msvc/vs2017/libsodium/libsodium.vcxproj.filters \
|
||||
|
msvc/vs2017/libsodium/libsodium.xml \
|
||||
|
msvc/vs2017/libsodium.import.props \
|
||||
|
msvc/vs2017/libsodium.import.xml \
|
||||
|
msvc/vs2017/libsodium.sln |
@ -0,0 +1,14 @@ |
|||||
|
@ECHO OFF |
||||
|
|
||||
|
CALL buildbase.bat ..\vs2017\libsodium.sln 15 |
||||
|
ECHO. |
||||
|
CALL buildbase.bat ..\vs2015\libsodium.sln 14 |
||||
|
ECHO. |
||||
|
CALL buildbase.bat ..\vs2013\libsodium.sln 12 |
||||
|
ECHO. |
||||
|
CALL buildbase.bat ..\vs2012\libsodium.sln 11 |
||||
|
ECHO. |
||||
|
CALL buildbase.bat ..\vs2010\libsodium.sln 10 |
||||
|
ECHO. |
||||
|
|
||||
|
PAUSE |
@ -0,0 +1,70 @@ |
|||||
|
@ECHO OFF |
||||
|
REM Usage: [buildbase.bat ..\vs2013\mysolution.sln 12] |
||||
|
|
||||
|
SET solution=%1 |
||||
|
SET version=%2 |
||||
|
SET log=build_%version%.log |
||||
|
SET tools=Microsoft Visual Studio %version%.0\VC\vcvarsall.bat |
||||
|
IF %version% == 15 SET tools=Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat |
||||
|
SET environment="%programfiles(x86)%\%tools%" |
||||
|
IF NOT EXIST %environment% SET environment="%programfiles%\%tools%" |
||||
|
IF NOT EXIST %environment% GOTO no_tools |
||||
|
|
||||
|
ECHO Building: %solution% |
||||
|
|
||||
|
CALL %environment% x86 > nul |
||||
|
ECHO Platform=x86 |
||||
|
|
||||
|
ECHO Configuration=DynDebug |
||||
|
msbuild /m /v:n /p:Configuration=DynDebug /p:Platform=Win32 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=DynRelease |
||||
|
msbuild /m /v:n /p:Configuration=DynRelease /p:Platform=Win32 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=LtcgDebug |
||||
|
msbuild /m /v:n /p:Configuration=LtcgDebug /p:Platform=Win32 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=LtcgRelease |
||||
|
msbuild /m /v:n /p:Configuration=LtcgRelease /p:Platform=Win32 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=StaticDebug |
||||
|
msbuild /m /v:n /p:Configuration=StaticDebug /p:Platform=Win32 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=StaticRelease |
||||
|
msbuild /m /v:n /p:Configuration=StaticRelease /p:Platform=Win32 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
|
||||
|
CALL %environment% x86_amd64 > nul |
||||
|
ECHO Platform=x64 |
||||
|
|
||||
|
ECHO Configuration=DynDebug |
||||
|
msbuild /m /v:n /p:Configuration=DynDebug /p:Platform=x64 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=DynRelease |
||||
|
msbuild /m /v:n /p:Configuration=DynRelease /p:Platform=x64 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=LtcgDebug |
||||
|
msbuild /m /v:n /p:Configuration=LtcgDebug /p:Platform=x64 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=LtcgRelease |
||||
|
msbuild /m /v:n /p:Configuration=LtcgRelease /p:Platform=x64 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=StaticDebug |
||||
|
msbuild /m /v:n /p:Configuration=StaticDebug /p:Platform=x64 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
ECHO Configuration=StaticRelease |
||||
|
msbuild /m /v:n /p:Configuration=StaticRelease /p:Platform=x64 %solution% >> %log% |
||||
|
IF errorlevel 1 GOTO error |
||||
|
|
||||
|
ECHO Complete: %solution% |
||||
|
GOTO end |
||||
|
|
||||
|
:error |
||||
|
ECHO *** ERROR, build terminated early, see: %log% |
||||
|
GOTO end |
||||
|
|
||||
|
:no_tools |
||||
|
ECHO *** ERROR, build tools not found: %tools% |
||||
|
|
||||
|
:end |
||||
|
|
@ -0,0 +1,21 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<_PropertySheetDisplayName>Common Settings</_PropertySheetDisplayName> |
||||
|
<CharacterSet>Unicode</CharacterSet> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ImportGroup Label="PropertySheets"> |
||||
|
<Import Project="$(Platform).props" /> |
||||
|
</ImportGroup> |
||||
|
|
||||
|
<ItemDefinitionGroup> |
||||
|
<ClCompile> |
||||
|
<MultiProcessorCompilation>true</MultiProcessorCompilation> |
||||
|
<PreprocessorDefinitions>UNICODE;_UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
|
<WarningLevel>Level3</WarningLevel> |
||||
|
</ClCompile> |
||||
|
</ItemDefinitionGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,16 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<_PropertySheetDisplayName>Dynamic Library</_PropertySheetDisplayName> |
||||
|
<DefaultLinkage>dynamic</DefaultLinkage> |
||||
|
<TargetExt>.dll</TargetExt> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemDefinitionGroup> |
||||
|
<ClCompile> |
||||
|
<PreprocessorDefinitions>_DLL;_WINDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
|
</ClCompile> |
||||
|
</ItemDefinitionGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,29 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
|
||||
|
<ImportGroup Label="PropertySheets"> |
||||
|
<Import Project="Common.props" /> |
||||
|
</ImportGroup> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<_PropertySheetDisplayName>Debug Settings</_PropertySheetDisplayName> |
||||
|
<DebugOrRelease>Debug</DebugOrRelease> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemDefinitionGroup> |
||||
|
<ClCompile> |
||||
|
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> |
||||
|
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat> |
||||
|
<FunctionLevelLinking>true</FunctionLevelLinking> |
||||
|
<Optimization>Disabled</Optimization> |
||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
|
</ClCompile> |
||||
|
<ResourceCompile> |
||||
|
<PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions> |
||||
|
</ResourceCompile> |
||||
|
<Link> |
||||
|
<GenerateDebugInformation>true</GenerateDebugInformation> |
||||
|
</Link> |
||||
|
</ItemDefinitionGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,21 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<_PropertySheetDisplayName>Console Debug Dynamic</_PropertySheetDisplayName> |
||||
|
<DefaultLinkage>dynamic</DefaultLinkage> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ImportGroup Label="PropertySheets"> |
||||
|
<Import Project="Debug.props" /> |
||||
|
<Import Project="EXE.props" /> |
||||
|
</ImportGroup> |
||||
|
|
||||
|
<ItemDefinitionGroup> |
||||
|
<ClCompile> |
||||
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> |
||||
|
<LinkIncremental>true</LinkIncremental> |
||||
|
</ClCompile> |
||||
|
</ItemDefinitionGroup> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<_PropertySheetDisplayName>Dynamic Debug Library</_PropertySheetDisplayName> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ImportGroup Label="PropertySheets"> |
||||
|
<Import Project="Debug.props" /> |
||||
|
<Import Project="DLL.props" /> |
||||
|
</ImportGroup> |
||||
|
|
||||
|
<ItemDefinitionGroup> |
||||
|
<ClCompile> |
||||
|
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary> |
||||
|
<LinkIncremental>true</LinkIncremental> |
||||
|
</ClCompile> |
||||
|
</ItemDefinitionGroup> |
||||
|
|
||||
|
</Project> |
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue