2 * Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
29 /* these functions are only needed if Perl has 32-bit IV's */
30 /* Make sure Math::BigInt is loaded
33 load_Math_BigInt(void)
35 static int loaded = 0;
39 eval_pv("use Math::BigInt; use Amanda::BigIntCompat;", 1);
43 /* Given a string, create a Math::BigInt representing its value.
45 * @param num: string representation of a number
46 * @returns: BigInt representation of the same number
63 XPUSHs(sv_2mortal(newSVpv("Math::BigInt", 0)));
64 XPUSHs(sv_2mortal(newSVpv(num, 0)));
67 count = call_method("new", G_SCALAR);
72 croak("Expected a result from Math::Bigint->new");
85 amglue_newSVi64(gint64 v)
88 g_snprintf(numstr, sizeof(numstr), "%jd", (intmax_t)v);
89 numstr[sizeof(numstr)-1] = '\0';
90 return str2bigint(numstr);
94 amglue_newSVu64(guint64 v)
97 g_snprintf(numstr, sizeof(numstr), "%ju", (uintmax_t)v);
98 numstr[sizeof(numstr)-1] = '\0';
99 return str2bigint(numstr);
106 /* Conversion from Perl values handles BigInts regardless of whether
107 * Perl's IVs are 32- or 64-bit, for completeness' sake.
110 /* Convert a bigint to a signed integer, or croak trying.
112 * @param bigint: the perl object to convert
113 * @returns: signed integer
116 bigint2int64(SV *bigint)
121 gboolean negative = FALSE;
125 /* first, see if it's a BigInt */
126 if (!sv_isobject(bigint) || !sv_derived_from(bigint, "Math::BigInt"))
127 croak("Expected an integer or a Math::BigInt; cannot convert");
133 * strtoull($bigint->bstr()) */
139 count = call_method("Math::BigInt::bstr", G_SCALAR);
144 croak("Expected a result from Math::BigInt::bstr");
147 str = SvPV_nolen(sv);
149 croak("Math::BigInt::bstr did not return a string");
157 absval = g_ascii_strtoull(str, NULL, 0);
158 /* (the last branch of this || depends on G_MININT64 = -G_MAXINT64-1) */
159 if ((absval == G_MAXUINT64 && errno == ERANGE)
160 || (!negative && absval > (guint64)(G_MAXINT64))
161 || (negative && absval > (guint64)(G_MAXINT64)+1))
162 croak("Expected a signed 64-bit value or smaller; value '%s' out of range", str);
164 croak("Math::BigInt->bstr returned invalid number '%s'", str);
170 if (negative) return -absval;
174 /* Convert bigint to an unsigned integer, or croak trying.
176 * @param bigint: the perl object to convert
177 * @returns: unsigned integer
180 bigint2uint64(SV *bigint)
188 /* first, see if it's a BigInt */
189 if (!sv_isobject(bigint) || !sv_derived_from(bigint, "Math::BigInt"))
190 croak("Expected an integer or a Math::BigInt; cannot convert");
195 /* make sure the bigint is positive:
196 * croak(..) unless $bigint->sign() eq "+"; */
202 count = call_method("Math::BigInt::sign", G_SCALAR);
207 croak("Expected a result from Math::BigInt::sign");
210 str = SvPV_nolen(sv);
212 croak("Math::BigInt::sign did not return a string");
214 if (strcmp(str, "+") != 0)
215 croak("Expected a positive number; value out of range");
218 * strtoull($bigint->bstr()) */
224 count = call_method("Math::BigInt::bstr", G_SCALAR);
229 croak("Expected a result from Math::BigInt::bstr");
232 str = SvPV_nolen(sv);
234 croak("Math::BigInt::bstr did not return a string");
237 rv = g_ascii_strtoull(str, NULL, 0);
238 if (rv == G_MAXUINT64 && errno == ERANGE)
239 croak("Expected an unsigned 64-bit value or smaller; value '%s' out of range", str);
241 croak("Math::BigInt->bstr returned invalid number '%s'", str);
250 gint64 amglue_SvI64(SV *sv)
258 } else if (SvNOK(sv)) {
259 double dv = SvNV(sv);
261 /* preprocessor constants seem to have trouble here, so we convert to gint64 and
262 * back, and if the result differs, then we have lost something. Note that this will
263 * also error out on integer truncation .. which is probably OK */
264 gint64 iv = (gint64)dv;
265 if (dv != (double)iv) {
266 croak("Expected a signed 64-bit value or smaller; value '%.0f' out of range", (float)dv);
272 return bigint2int64(sv);
276 guint64 amglue_SvU64(SV *sv)
281 } else if (SvIV(sv) < 0) {
282 croak("Expected an unsigned value, got a negative integer");
285 return (guint64)SvIV(sv);
287 } else if (SvNOK(sv)) {
288 double dv = SvNV(sv);
290 croak("Expected an unsigned value, got a negative integer");
292 } else if (dv > (double)G_MAXUINT64) {
293 croak("Expected an unsigned 64-bit value or smaller; value out of range");
299 return bigint2uint64(sv);
303 gint32 amglue_SvI32(SV *sv)
305 gint64 v64 = amglue_SvI64(sv);
306 if (v64 < G_MININT32 || v64 > G_MAXINT32) {
307 croak("Expected a 32-bit integer; value out of range");
314 guint32 amglue_SvU32(SV *sv)
316 guint64 v64 = amglue_SvU64(sv);
317 if (v64 > G_MAXUINT32) {
318 croak("Expected a 32-bit unsigned integer; value out of range");
325 gint16 amglue_SvI16(SV *sv)
327 gint64 v64 = amglue_SvI64(sv);
328 if (v64 < G_MININT16 || v64 > G_MAXINT16) {
329 croak("Expected a 16-bit integer; value out of range");
336 guint16 amglue_SvU16(SV *sv)
338 guint64 v64 = amglue_SvU64(sv);
339 if (v64 > G_MAXUINT16) {
340 croak("Expected a 16-bit unsigned integer; value out of range");
347 gint8 amglue_SvI8(SV *sv)
349 gint64 v64 = amglue_SvI64(sv);
350 if (v64 < G_MININT8 || v64 > G_MAXINT8) {
351 croak("Expected a 8-bit integer; value out of range");
358 guint8 amglue_SvU8(SV *sv)
360 guint64 v64 = amglue_SvU64(sv);
361 if (v64 > G_MAXUINT8) {
362 croak("Expected a 8-bit unsigned integer; value out of range");