summaryrefslogtreecommitdiffstats
path: root/pbn_init.c
diff options
context:
space:
mode:
Diffstat (limited to 'pbn_init.c')
-rw-r--r--pbn_init.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/pbn_init.c b/pbn_init.c
new file mode 100644
index 0000000..53edb87
--- /dev/null
+++ b/pbn_init.c
@@ -0,0 +1,164 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * Copyright 2007 H. Peter Anvin - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation, Inc.,
+ * 59 Temple Place Ste 330, Boston MA 02111-1307, USA; version 2.1,
+ * incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+/*
+ * pbn_int.c
+ */
+
+#define PBN_INIT 1
+#include <string.h>
+#include "pbnint.h"
+
+struct pbn *pbn_new(int len)
+{
+ struct pbn *pbn;
+
+ pbn = _pbn_zalloc(sizeof(struct pbn)+(len-1)*sizeof(pbn_atom_t));
+ if (!pbn)
+ return NULL;
+
+ pbn->ref = 1;
+ pbn->len = len;
+
+ return pbn;
+}
+
+void pbn_free(struct pbn *pbn)
+{
+ if (! --pbn->ref)
+ _pbn_free(pbn);
+}
+
+#if PBN_ATOM_BITS != 32 && PBN_ATOM_BITS != 64
+# error "Fix pbn_ilog2p1 for new PBN_ATOM_BITS size"
+#endif
+
+static int pbn_ilog2p1(pbn_atom_t v)
+{
+ int p = 1;
+
+#if PBN_ATOM_BITS >= 64
+ if (v & 0xffffffff00000000) {
+ p += 32;
+ v >>= 32;
+ }
+#endif
+
+ if (v & 0xffff0000) {
+ p += 16;
+ v >>= 16;
+ }
+ if (v & 0xff00) {
+ p += 8;
+ v >>= 8;
+ }
+ if (v & 0xf0) {
+ p += 4;
+ v >>= 4;
+ }
+ if (v & 0xc) {
+ p += 2;
+ v >>= 2;
+ }
+ if (v & 0x2) {
+ p += 1;
+ v >>= 1;
+ }
+
+ return p;
+}
+
+struct pbn *pbn_int(pbn_satom_t v)
+{
+ struct pbn *pbn = pbn_new(1);
+ if (!pbn)
+ return NULL;
+
+ if (v < 0) {
+ v = -v;
+ pbn->minus = 1;
+ } else {
+ pbn->minus = 0;
+ }
+
+ pbn->num[0] = v;
+ pbn->bits = pbn_ilog2p1(v);
+ return pbn;
+}
+
+/* Produce a duplicate */
+struct pbn *pbn_dup(const struct pbn *src)
+{
+ int bytes = sizeof(struct pbn)+(src->len-1)*sizeof(pbn_atom_t);
+ struct pbn *dst = _pbn_malloc(bytes);
+
+ memcpy(dst, src, bytes);
+ dst->ref = 1;
+
+ return dst;
+}
+
+/* Produce a duplicate, possibly extended */
+struct pbn *pbn_dupx(const struct pbn *src, int len)
+{
+ int slack = src->len >= len ? 0 : len-src->len;
+ int bytes = sizeof(struct pbn)+(src->len-1)*sizeof(pbn_atom_t);
+ struct pbn *dst = _pbn_malloc(bytes+slack*sizeof(pbn_atom_t));
+
+ if (!dst)
+ return NULL;
+
+ memcpy(dst, src, bytes);
+ dst->ref = 1;
+ memset(&dst->num[slack], 0, slack*sizeof(pbn_atom_t));
+
+ return dst;
+}
+
+/* Returns a duplicate or clears src for reuse, consumes a reference */
+struct pbn *pbn_cow(struct pbn *src, int len)
+{
+ int olen = src->len;
+
+ if (src->ref <= 1) {
+ src->ref = 1;
+ if (len > olen) {
+ src = _pbn_realloc(src, sizeof(struct pbn)+
+ (len-1)*sizeof(pbn_atom_t));
+ src->len = len;
+ }
+ memset(&src->num[olen], 0, (len-olen)*sizeof(pbn_atom_t));
+ return src;
+ }
+
+ src->ref--;
+ return pbn_dupx(src, len);
+}
+
+/* Adjust the bits field of a specific pbn, and clear the minus flag
+ if the value is actually zero. */
+void _pbn_adjust_bits(struct pbn *pbn)
+{
+ int i;
+ pbn_atom_t v;
+
+ for (i = pbn->len-1; i >= 0; i--) {
+ v = pbn->num[i];
+ if (v) {
+ pbn->bits = i*PBN_ATOM_BITS + pbn_ilog2p1(v);
+ return;
+ }
+ }
+
+ pbn->bits = 0;
+ pbn->minus = 0;
+}