summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Browning <rlb@defaultvalue.org>2003-04-04 21:51:35 +0000
committerRob Browning <rlb@defaultvalue.org>2003-04-04 21:51:35 +0000
commit2de4f939ab7cbaea3b16949cd0d18df99d99f947 (patch)
treea6fb112471a84524736742bb029ce63739c4f101
parent969d3bd0433b5bbaf88f113fbec50bb88c0a2aa2 (diff)
downloadguile-2de4f939ab7cbaea3b16949cd0d18df99d99f947.tar.gz
* socket.c (FLIPCPY_NET_HOST_128): new macro.
(ipv6_net_to_num, ipv6_num_to_net, bignum_in_ipv6_range_p): rewrite to handle GMP bignums.
-rw-r--r--libguile/socket.c167
1 files changed, 127 insertions, 40 deletions
diff --git a/libguile/socket.c b/libguile/socket.c
index 336784e72..47f548262 100644
--- a/libguile/socket.c
+++ b/libguile/socket.c
@@ -47,6 +47,7 @@
#endif
#include <errno.h>
+#include <gmp.h> /* is this even remotely right? -- probably not... */
#include "libguile/_scm.h"
#include "libguile/unif.h"
@@ -268,7 +269,7 @@ SCM_DEFINE (scm_inet_makeaddr, "inet-makeaddr", 2, 0, 0,
\
for (i = 0; i < 8; i++)\
{\
- char c = (addr)[i];\
+ scm_t_uint8 c = (addr)[i];\
\
(addr)[i] = (addr)[15 - i];\
(addr)[15 - i] = c;\
@@ -276,63 +277,152 @@ SCM_DEFINE (scm_inet_makeaddr, "inet-makeaddr", 2, 0, 0,
}
#endif
+#ifdef WORDS_BIGENDIAN
+#define FLIPCPY_NET_HOST_128(dest, src) memcpy (dest, src, 16)
+#else
+#define FLIPCPY_NET_HOST_128(dest, src) \
+{ \
+ const scm_t_uint8 *tmp_srcp = (src) + 15; \
+ scm_t_uint8 *tmp_destp = (dest); \
+ \
+ do { \
+ *tmp_destp++ = *tmp_srcp--; \
+ } while (tmp_srcp != (src)); \
+}
+#endif
+
+
+#if (SIZEOF_SCM_T_BITS * 8) > 128
+#error "Assumption that scm_t_bits <= 128 bits has been violated."
+#endif
+
+#if (SIZEOF_UNSIGNED_LONG * 8) > 128
+#error "Assumption that unsigned long <= 128 bits has been violated."
+#endif
+
+#if (SIZEOF_UNSIGNED_LONG_LONG * 8) > 128
+#error "Assumption that unsigned long long <= 128 bits has been violated."
+#endif
+
/* convert a 128 bit IPv6 address in network order to a host ordered
SCM integer. */
-static SCM ipv6_net_to_num (const char *src)
+static SCM ipv6_net_to_num (const scm_t_uint8 *src)
{
- int big_digits = 128 / SCM_BITSPERDIG;
- const int bytes_per_dig = SCM_BITSPERDIG / 8;
- char addr[16];
- char *ptr = addr;
- SCM result;
-
- memcpy (addr, src, 16);
- /* get rid of leading zeros. */
- while (big_digits > 0)
- {
- long test = 0;
+ int i = 0;
+ const scm_t_uint8 *ptr = src;
+ int num_zero_bytes = 0;
+ scm_t_uint8 addr[16];
- memcpy (&test, ptr, bytes_per_dig);
- if (test != 0)
- break;
- ptr += bytes_per_dig;
- big_digits--;
- }
- FLIP_NET_HOST_128 (addr);
- if (big_digits * bytes_per_dig <= sizeof (unsigned long))
+ /* count leading zeros (since we know it's bigendian, they'll be first) */
+ while (i < 16)
{
- /* this is just so that we use INUM where possible. */
- unsigned long l_addr;
+ if (*ptr) break;
+ num_zero_bytes++;
+ i++;
+ }
- memcpy (&l_addr, addr, sizeof (unsigned long));
- result = scm_ulong2num (l_addr);
+ if (SCM_SIZEOF_UNSIGNED_LONG_LONG != 0) /* compiler should optimize this */
+ {
+ if ((16 - num_zero_bytes) <= sizeof (unsigned long long))
+ {
+ /* it fits */
+ unsigned long long x;
+
+ FLIPCPY_NET_HOST_128(addr, src);
+#ifdef WORDS_BIGENDIAN
+ memcpy (&x, addr + (16 - sizeof (x)), sizeof (x));
+#else
+ memcpy (&x, addr, sizeof (x));
+#endif
+ return scm_ulong_long2num (x);
+ }
}
else
{
- result = scm_i_mkbig (big_digits, 0);
- memcpy (SCM_BDIGITS (result), addr, big_digits * bytes_per_dig);
+ if ((16 - num_zero_bytes) <= sizeof (unsigned long))
+ {
+ /* this is just so that we use INUM where possible. */
+ unsigned long x;
+
+ FLIPCPY_NET_HOST_128(addr, src);
+#ifdef WORDS_BIGENDIAN
+ memcpy (&x, addr + (16 - sizeof (x)), sizeof (x));
+#else
+ memcpy (&x, addr, sizeof (x));
+#endif
+ return scm_ulong2num (x);
+ }
}
- return result;
+ /* otherwise get the big hammer */
+ {
+ SCM result = scm_i_mkbig ();
+
+ mpz_import (SCM_I_BIG_MPZ (result),
+ 1, /* chunk */
+ 1, /* big-endian chunk ordering */
+ 16, /* chunks are 16 bytes long */
+ 1, /* big-endian byte ordering */
+ 0, /* "nails" -- leading unused bits per chunk */
+ src);
+ return scm_i_normbig (result);
+ }
}
/* convert a host ordered SCM integer to a 128 bit IPv6 address in
network order. */
-static void ipv6_num_to_net (SCM src, char *dst)
+static void ipv6_num_to_net (SCM src, scm_t_uint8 *dst)
{
+ /* This code presumes that src has already been checked for range. */
if (SCM_INUMP (src))
{
- scm_t_uint32 addr = htonl (SCM_INUM (src));
-
- memset (dst, 0, 12);
- memcpy (dst + 12, &addr, 4);
+ scm_t_signed_bits n = SCM_INUM (src);
+#ifdef WORDS_BIGENDIAN
+ memset (dst, 0, 16 - sizeof (scm_t_signed_bits));
+ memcpy (dst + (16 - sizeof (scm_t_signed_bits)),
+ &n,
+ sizeof (scm_t_signed_bits));
+#else
+ memset (dst + sizeof (scm_t_signed_bits),
+ 0,
+ 16 - sizeof (scm_t_signed_bits));
+ /* FIXME: this pair of ops is kinda wasteful -- should rewrite as
+ a single loop perhaps, similar to the handling of bignums. */
+ memcpy (dst, &n, sizeof (scm_t_signed_bits));
+ FLIP_NET_HOST_128 (dst);
+#endif
}
else
{
+ /* Presumes src has already been checked for fit -- see above. */
+ size_t count;
memset (dst, 0, 16);
- memcpy (dst, SCM_BDIGITS (src),
- SCM_NUMDIGS (src) * (SCM_BITSPERDIG / 8));
- FLIP_NET_HOST_128 (dst);
+ mpz_export (dst,
+ &count,
+ 1, /* big-endian chunk ordering */
+ 16, /* chunks are 16 bytes long */
+ 1, /* big-endian byte ordering */
+ 0, /* "nails" -- leading unused bits per chunk */
+ SCM_I_BIG_MPZ (src));
+ scm_remember_upto_here_1 (src);
+ }
+}
+
+static int
+bignum_in_ipv6_range_p (SCM address)
+{
+ int result;
+ int sgn = mpz_sgn (SCM_I_BIG_MPZ (address));
+
+ if (sgn < 0)
+ result = 0;
+ else
+ {
+ int size = mpz_sizeinbase (SCM_I_BIG_MPZ (address), 2);
+ if (size > 128) result = 0;
+ else result = 1;
}
+ scm_remember_upto_here_1 (address);
+ return result;
}
/* check that an SCM variable contains an IPv6 integer address. */
@@ -342,10 +432,7 @@ static void ipv6_num_to_net (SCM src, char *dst)
else\
{\
SCM_VALIDATE_BIGINT (which_arg, address);\
- SCM_ASSERT_RANGE (which_arg, address,\
- !SCM_BIGSIGN (address)\
- && (SCM_BITSPERDIG\
- * SCM_NUMDIGS (address) <= 128));\
+ SCM_ASSERT_RANGE (which_arg, address, bignum_in_ipv6_range_p);\
}
#ifdef HAVE_INET_PTON