Skip to content

Commit

Permalink
add resizeable scratch space API
Browse files Browse the repository at this point in the history
Alignment support by Pieter Wuille.
  • Loading branch information
apoelstra authored and jonasnick committed Dec 7, 2017
1 parent 6ad5cdb commit 548de42
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ noinst_HEADERS += src/field_5x52_asm_impl.h
noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h
noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h
noinst_HEADERS += src/util.h
noinst_HEADERS += src/scratch.h
noinst_HEADERS += src/scratch_impl.h
noinst_HEADERS += src/testrand.h
noinst_HEADERS += src/testrand_impl.h
noinst_HEADERS += src/hash.h
Expand Down
35 changes: 35 additions & 0 deletions include/secp256k1.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ extern "C" {
*/
typedef struct secp256k1_context_struct secp256k1_context;

/** Opaque data structure that holds rewriteable "scratch space"
*
* The purpose of this structure is to replace dynamic memory allocations,
* because we target architectures where this may not be available. It is
* essentially a resizable (within specified parameters) block of bytes,
* which is initially created either by memory allocation or TODO as a pointer
* into some fixed rewritable space.
*
* Unlike the context object, this cannot safely be shared between threads
* without additional synchronization logic.
*/
typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space;

/** Opaque data structure that holds a parsed and valid public key.
*
* The exact representation of data inside is implementation defined and not
Expand Down Expand Up @@ -243,6 +256,28 @@ SECP256K1_API void secp256k1_context_set_error_callback(
const void* data
) SECP256K1_ARG_NONNULL(1);

/** Create a secp256k1 scratch space object.
*
* Returns: a newly created scratch space.
* Args: ctx: an existing context object (cannot be NULL)
* In: init_size: initial amount of memory to allocate
* max_size: maximum amount of memory to allocate
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT secp256k1_scratch_space* secp256k1_scratch_space_create(
const secp256k1_context* ctx,
size_t init_size,
size_t max_size
) SECP256K1_ARG_NONNULL(1);

/** Destroy a secp256k1 scratch space.
*
* The pointer may not be used afterwards.
* Args: scratch: space to destroy
*/
SECP256K1_API void secp256k1_scratch_space_destroy(
secp256k1_scratch_space* scratch
);

/** Parse a variable-length public key into the pubkey object.
*
* Returns: 1 if the public key was fully valid.
Expand Down
35 changes: 35 additions & 0 deletions src/scratch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**********************************************************************
* Copyright (c) 2017 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

#ifndef _SECP256K1_SCRATCH_
#define _SECP256K1_SCRATCH_

/* The typedef is used internally; the struct name is used in the public API
* (where it is exposed as a different typedef) */
typedef struct secp256k1_scratch_space_struct {
void *data;
size_t offset;
size_t init_size;
size_t max_size;
const secp256k1_callback* error_callback;
} secp256k1_scratch;

static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t init_size, size_t max_size);
static void secp256k1_scratch_destroy(secp256k1_scratch* scratch);

/** Returns the maximum allocation the scratch space will allow */
static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects);

/** Attempts to allocate so that there are `n` available bytes. Returns 1 on success, 0 on failure */
static int secp256k1_scratch_resize(secp256k1_scratch* scratch, size_t n, size_t n_objects);

/** Returns a pointer into the scratch space or NULL if there is insufficient available space */
static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n);

/** Resets the returned pointer to the beginning of space */
static void secp256k1_scratch_reset(secp256k1_scratch* scratch);

#endif
77 changes: 77 additions & 0 deletions src/scratch_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**********************************************************************
* Copyright (c) 2017 Andrew Poelstra *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/

#ifndef _SECP256K1_SCRATCH_IMPL_H_
#define _SECP256K1_SCRATCH_IMPL_H_

#include "scratch.h"

/* Using 16 bytes alignment because common architectures never have alignment
* requirements above 8 for any of the types we care about. In addition we
* leave some room because currently we don't care about a few bytes.
* TODO: Determine this at configure time. */
#define ALIGNMENT 16

static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t init_size, size_t max_size) {
secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret));
if (ret != NULL) {
ret->data = checked_malloc(error_callback, init_size);
if (ret->data == NULL) {
free (ret);
return NULL;
}
ret->offset = 0;
ret->init_size = init_size;
ret->max_size = max_size;
ret->error_callback = error_callback;
}
return ret;
}

static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) {
if (scratch != NULL) {
free(scratch->data);
free(scratch);
}
}

static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) {
if (scratch->max_size <= objects * ALIGNMENT) {
return 0;
}
return scratch->max_size - objects * ALIGNMENT;
}

static int secp256k1_scratch_resize(secp256k1_scratch* scratch, size_t n, size_t objects) {
n += objects * ALIGNMENT;
if (n > scratch->init_size && n <= scratch->max_size) {
void *tmp = checked_realloc(scratch->error_callback, scratch->data, n);
if (tmp == NULL) {
return 0;
}
scratch->init_size = n;
scratch->data = tmp;
}
return n <= scratch->max_size;
}

static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) {
void *ret;
size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT;
if (size + scratch->offset > scratch->init_size) {
return NULL;
}
ret = (void *) ((unsigned char *) scratch->data + scratch->offset);
memset(ret, 0, size);
scratch->offset += size;
return ret;
}

static void secp256k1_scratch_reset(secp256k1_scratch* scratch) {
scratch->offset = 0;
}

#endif
12 changes: 12 additions & 0 deletions src/secp256k1.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "ecdsa_impl.h"
#include "eckey_impl.h"
#include "hash_impl.h"
#include "scratch_impl.h"

#define ARG_CHECK(cond) do { \
if (EXPECT(!(cond), 0)) { \
Expand Down Expand Up @@ -114,6 +115,17 @@ void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(co
ctx->error_callback.data = data;
}

secp256k1_scratch_space* secp256k1_scratch_space_create(const secp256k1_context* ctx, size_t init_size, size_t max_size) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(max_size >= init_size);

return secp256k1_scratch_create(&ctx->error_callback, init_size, max_size);
}

void secp256k1_scratch_space_destroy(secp256k1_scratch_space* scratch) {
secp256k1_scratch_destroy(scratch);
}

static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) {
if (sizeof(secp256k1_ge_storage) == 64) {
/* When the secp256k1_ge_storage type is exactly 64 byte, use its
Expand Down
36 changes: 36 additions & 0 deletions src/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,41 @@ void run_context_tests(void) {
secp256k1_context_destroy(NULL);
}

void run_scratch_tests(void) {
int32_t ecount = 0;
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
secp256k1_scratch_space *scratch;

/* Test public API */
secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount);
scratch = secp256k1_scratch_space_create(none, 100, 10);
CHECK(scratch == NULL);
CHECK(ecount == 1);

scratch = secp256k1_scratch_space_create(none, 100, 100);
CHECK(scratch != NULL);
CHECK(ecount == 1);
secp256k1_scratch_space_destroy(scratch);

scratch = secp256k1_scratch_space_create(none, 100, 1000);
CHECK(scratch != NULL);
CHECK(ecount == 1);

/* Test internal API */
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);
CHECK(secp256k1_scratch_max_allocation(scratch, 1) < 1000);
CHECK(secp256k1_scratch_resize(scratch, 50, 1) == 1); /* no-op */
CHECK(secp256k1_scratch_resize(scratch, 200, 1) == 1);
CHECK(secp256k1_scratch_resize(scratch, 950, 1) == 1);
CHECK(secp256k1_scratch_resize(scratch, 1000, 1) == 0);
CHECK(secp256k1_scratch_resize(scratch, 2000, 1) == 0);
CHECK(secp256k1_scratch_max_allocation(scratch, 0) == 1000);

/* cleanup */
secp256k1_scratch_space_destroy(scratch);
secp256k1_context_destroy(none);
}

/***** HASH TESTS *****/

void run_sha256_tests(void) {
Expand Down Expand Up @@ -4451,6 +4486,7 @@ int main(int argc, char **argv) {

/* initialize */
run_context_tests();
run_scratch_tests();
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
if (secp256k1_rand_bits(1)) {
secp256k1_rand256(run32);
Expand Down
8 changes: 8 additions & 0 deletions src/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_
return ret;
}

static SECP256K1_INLINE void *checked_realloc(const secp256k1_callback* cb, void *ptr, size_t size) {
void *ret = realloc(ptr, size);
if (ret == NULL) {
secp256k1_callback_call(cb, "Out of memory");
}
return ret;
}

/* Macro for restrict, when available and not in a VERIFY build. */
#if defined(SECP256K1_BUILD) && defined(VERIFY)
# define SECP256K1_RESTRICT
Expand Down

0 comments on commit 548de42

Please sign in to comment.