From 248eb1c5dc7f2ee86808875e42e3a38bd4a510df Mon Sep 17 00:00:00 2001 From: Alex Dyachenko Date: Fri, 11 Apr 2014 21:09:39 -0400 Subject: [PATCH] Raw binary IO. This necessitated some refactoring to the MPIR code. --- .../mpir.net-tests/mpir.net-tests.csproj | 1 + gmp-h.in | 16 +++++ mpir.net/mpir.net-tests/HugeIntTests/IO.cs | 46 +++++++++++++ mpir.net/mpir.net/Common.cpp | 8 ++- mpir.net/mpir.net/Common.h | 1 + mpir.net/mpir.net/HugeInt.cpp | 65 ++++++++++++++++++- mpir.net/mpir.net/HugeInt.h | 22 +++++++ mpz/inp_raw.c | 51 +++++++++++---- mpz/out_raw.c | 26 ++++++-- 9 files changed, 212 insertions(+), 24 deletions(-) create mode 100644 mpir.net/mpir.net-tests/HugeIntTests/IO.cs diff --git a/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj b/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj index 09bb160a..312320dd 100644 --- a/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj +++ b/build.vc11/mpir.net/mpir.net-tests/mpir.net-tests.csproj @@ -100,6 +100,7 @@ + diff --git a/gmp-h.in b/gmp-h.in index abd1b79f..87c25ab7 100644 --- a/gmp-h.in +++ b/gmp-h.in @@ -300,6 +300,17 @@ typedef struct } __gmp_randstate_struct; typedef __gmp_randstate_struct gmp_randstate_t[1]; +/* Output of mp?_out_raw_m */ +typedef struct +{ + unsigned char* allocated; + size_t allocatedSize; + unsigned char* written; + size_t writtenSize; +} __mpir_out_struct; +typedef __mpir_out_struct mpir_out_struct[1]; +typedef __mpir_out_struct *mpir_out_ptr; + /* Types for function declarations in gmp files. */ /* ??? Should not pollute user name space with these ??? */ typedef __gmp_const __mpz_struct *mpz_srcptr; @@ -967,6 +978,9 @@ __GMP_DECLSPEC int mpz_init_set_str __GMP_PROTO ((mpz_ptr, __gmp_const char *, i #define mpz_init_set_ui __gmpz_init_set_ui __GMP_DECLSPEC void mpz_init_set_ui __GMP_PROTO ((mpz_ptr, mpir_ui)); +__GMP_DECLSPEC void mpz_inp_raw_p __GMP_PROTO ((mpz_ptr x, unsigned char* csize_bytes, mpir_out_ptr out)); +__GMP_DECLSPEC void mpz_inp_raw_m __GMP_PROTO ((mpz_ptr x, mpir_out_ptr out)); + #define mpz_inp_raw __gmpz_inp_raw #ifdef _GMP_H_HAVE_FILE __GMP_DECLSPEC size_t mpz_inp_raw __GMP_PROTO ((mpz_ptr, FILE *)); @@ -1052,6 +1066,8 @@ __GMP_DECLSPEC void mpz_nextprime __GMP_PROTO ((mpz_ptr, mpz_srcptr)); #define mpz_next_prime_candidate __gmpz_next_prime_candidate __GMP_DECLSPEC void mpz_next_prime_candidate __GMP_PROTO ((mpz_ptr, mpz_srcptr, gmp_randstate_t)); +__GMP_DECLSPEC void mpz_out_raw_m __GMP_PROTO ((mpir_out_ptr, mpz_srcptr)); + #define mpz_out_raw __gmpz_out_raw #ifdef _GMP_H_HAVE_FILE __GMP_DECLSPEC size_t mpz_out_raw __GMP_PROTO ((FILE *, mpz_srcptr)); diff --git a/mpir.net/mpir.net-tests/HugeIntTests/IO.cs b/mpir.net/mpir.net-tests/HugeIntTests/IO.cs new file mode 100644 index 00000000..ba1ca0f4 --- /dev/null +++ b/mpir.net/mpir.net-tests/HugeIntTests/IO.cs @@ -0,0 +1,46 @@ +/* +Copyright 2014 Alex Dyachenko + +This file is part of the MPIR Library. + +The MPIR Library 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; either version 3 of the License, or (at +your option) any later version. + +The MPIR Library 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 Lesser General Public +License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the MPIR Library. If not, see http://www.gnu.org/licenses/. +*/ + +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MPIR.Tests.HugeIntTests +{ + [TestClass] + public class IO + { + [TestMethod] + public void InputOutputRaw() + { + using (var a = new HugeInt("0x10123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")) + using (var b = new HugeInt()) + using (var ms = new MemoryStream()) + { + a.Value = a ^ 100; + a.Write(ms); + ms.Position = 0; + b.Read(ms); + Assert.AreEqual(a, b); + Assert.AreEqual(ms.Length, ms.Position); + } + } + //more tests coming here + } +} diff --git a/mpir.net/mpir.net/Common.cpp b/mpir.net/mpir.net/Common.cpp index 75a24183..7a6043ab 100644 --- a/mpir.net/mpir.net/Common.cpp +++ b/mpir.net/mpir.net/Common.cpp @@ -18,13 +18,17 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. */ #include "Stdafx.h" -#include "Common.h" void CustomFree (void* ptr) +{ + CustomFree(ptr, 0); +} + +void CustomFree(void* ptr, size_t size) { void (*freeFunc) (void*, size_t); mp_get_memory_functions (NULL, NULL, &freeFunc); - freeFunc(ptr, 0); + freeFunc(ptr, size); } void* CustomAllocate(size_t size) diff --git a/mpir.net/mpir.net/Common.h b/mpir.net/mpir.net/Common.h index a107fbe3..6c9944e8 100644 --- a/mpir.net/mpir.net/Common.h +++ b/mpir.net/mpir.net/Common.h @@ -22,6 +22,7 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. void* CustomAllocate (size_t alloc_size); void* CustomReallocate (void* ptr, size_t new_size); void CustomFree (void* ptr); +void CustomFree (void* ptr, size_t size); enum EvaluationOptions : __int8 { diff --git a/mpir.net/mpir.net/HugeInt.cpp b/mpir.net/mpir.net/HugeInt.cpp index 2d103d73..4f431e29 100644 --- a/mpir.net/mpir.net/HugeInt.cpp +++ b/mpir.net/mpir.net/HugeInt.cpp @@ -18,7 +18,6 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. */ #include "Stdafx.h" -#include "Common.h" #define DEFINE_ASSIGNMENT_PROLOG(name) void Mpir##name##Expression::AssignTo(mpz_ptr destination) @@ -504,4 +503,68 @@ namespace MPIR } #pragma endregion + + #pragma region IO + + #define chunkSize 1024 + + void HugeInt::Write(Stream^ stream) + { + mpir_out_struct out; + mpz_out_raw_m(out, _value); + + auto buffer = gcnew array(chunkSize); + auto ptr = out->written; + + while(out->writtenSize > 0) + { + auto len = Math::Min(chunkSize, (int)out->writtenSize); + Marshal::Copy(IntPtr(ptr), buffer, 0, len); + stream->Write(buffer, 0, len); + ptr += len; + out->writtenSize -= len; + } + + CustomFree(out->allocated, out->allocatedSize); + } + + void HugeInt::Read(Stream^ stream) + { + unsigned char csize_bytes[4]; + mpir_out_struct out; + + /* 4 bytes for size */ + for(int i = 0; i < 4; i++) + { + auto byte = stream->ReadByte(); + if(byte < 0) + throw gcnew Exception("Unexpected end of stream"); + + csize_bytes[i] = byte; + } + + mpz_inp_raw_p(_value, csize_bytes, out); + + if(out->writtenSize != 0) + { + auto buffer = gcnew array(chunkSize); + auto ptr = out->written; + auto toRead = (int)out->writtenSize; + + while(toRead > 0) + { + auto len = Math::Min(chunkSize, toRead); + if (len != stream->Read(buffer, 0, len)) + throw gcnew Exception("Unexpected end of stream"); + + Marshal::Copy(buffer, 0, IntPtr(ptr), len); + ptr += len; + toRead -= len; + } + + mpz_inp_raw_m(_value, out); + } + } + + #pragma endregion }; \ No newline at end of file diff --git a/mpir.net/mpir.net/HugeInt.h b/mpir.net/mpir.net/HugeInt.h index 038d96d8..87bf9290 100644 --- a/mpir.net/mpir.net/HugeInt.h +++ b/mpir.net/mpir.net/HugeInt.h @@ -20,6 +20,7 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. #pragma once using namespace System; +using namespace System::IO; using namespace System::Runtime::InteropServices; #define IS_NULL(a) (Object::ReferenceEquals(a, nullptr)) @@ -1742,6 +1743,27 @@ namespace MPIR /// The number of digits the number would take written in the specified base, possibly 1 too big, not counting a leading minus. mp_size_t ApproximateSizeInBase(int base) { return mpz_sizeinbase(_value, base); } + /// + /// Output the integer to the in raw binary format. + /// The number is written in a portable format, with 4 bytes of size information, and that many bytes of limbs. + /// Both the size and the limbs are written in decreasing significance order (i.e., in big-endian). + /// The output can be read with Read(Stream). + /// The output cannot be read by mpz_inp_raw from GMP 1, because of changes necessary + /// for compatibility between 32-bit and 64-bit machines. + /// + /// Stream to output the number to + void Write(Stream^ stream); + + /// + /// Reads the integer value from the in raw binary format, as it would have been written by Write(Stream). + /// The number is read in a portable format, with 4 bytes of size information, and that many bytes of limbs. + /// Both the size and the limbs are written in decreasing significance order (i.e., in big-endian). + /// This routine can read the output from mpz_out_raw also from GMP 1, in spite of changes + /// necessary for compatibility between 32-bit and 64-bit machines. + /// + /// Stream to input the number from + void Read(Stream^ stream); + #pragma endregion }; }; \ No newline at end of file diff --git a/mpz/inp_raw.c b/mpz/inp_raw.c index a34c0c00..472770e2 100644 --- a/mpz/inp_raw.c +++ b/mpz/inp_raw.c @@ -52,11 +52,7 @@ size_t mpz_inp_raw (mpz_ptr x, FILE *fp) { unsigned char csize_bytes[4]; - mp_size_t csize, abs_xsize, i; - size_t abs_csize; - char *cp; - mp_ptr xp, sp, ep; - mp_limb_t slimb, elimb; + mpir_out_struct out; if (fp == 0) fp = stdin; @@ -65,6 +61,24 @@ mpz_inp_raw (mpz_ptr x, FILE *fp) if (fread (csize_bytes, sizeof (csize_bytes), 1, fp) != 1) return 0; + mpz_inp_raw_p(x, csize_bytes, out); + + if(out->writtenSize != 0) + { + if (fread (out->written, out->writtenSize, 1, fp) != 1) + return 0; + + mpz_inp_raw_m(x, out); + } + return out->writtenSize + 4; +} + +void mpz_inp_raw_p (mpz_ptr x, unsigned char* csize_bytes, mpir_out_ptr out) +{ + mp_size_t csize, abs_xsize, i; + size_t abs_csize; + mp_ptr xp; + csize = ( (mp_size_t) csize_bytes[0] << 24) + ((mp_size_t) csize_bytes[1] << 16) @@ -93,10 +107,22 @@ mpz_inp_raw (mpz_ptr x, FILE *fp) /* Get limb boundaries right in the read, for the benefit of the non-nails case. */ xp[0] = 0; - cp = (char *) (xp + abs_xsize) - abs_csize; - if (fread (cp, abs_csize, 1, fp) != 1) - return 0; + out->written = (unsigned char *) (xp + abs_xsize) - abs_csize; + } + out->writtenSize = abs_csize; + out->allocatedSize = abs_xsize; + SIZ(x) = (csize >= 0 ? abs_xsize : -abs_xsize); +} +void mpz_inp_raw_m(mpz_ptr x, mpir_out_ptr out) +{ + mp_ptr xp, sp, ep; + mp_size_t abs_xsize, i; + mp_limb_t slimb, elimb; + + abs_xsize = out->allocatedSize; + + xp = PTR(x); if (GMP_NAIL_BITS == 0) { /* Reverse limbs to least significant first, and byte swap. If @@ -128,9 +154,9 @@ mpz_inp_raw (mpz_ptr x, FILE *fp) limb = 0; bits = 0; tpos = 0; - for (i = abs_csize-1; i >= 0; i--) + for (i = out->writtenSize-1; i >= 0; i--) { - byte = (unsigned char) cp[i]; + byte = out->written[i]; limb |= (byte << bits); bits += 8; if (bits >= GMP_NUMB_BITS) @@ -157,8 +183,5 @@ mpz_inp_raw (mpz_ptr x, FILE *fp) limbs resulting from this. Should be a non-zero value here, but for safety don't assume that. */ MPN_NORMALIZE (xp, abs_xsize); - } - - SIZ(x) = (csize >= 0 ? abs_xsize : -abs_xsize); - return abs_csize + 4; + SIZ(x) = (SIZ(x) >= 0 ? abs_xsize : -abs_xsize); } diff --git a/mpz/out_raw.c b/mpz/out_raw.c index d50a5d30..0e7801c7 100644 --- a/mpz/out_raw.c +++ b/mpz/out_raw.c @@ -47,9 +47,7 @@ MA 02110-1301, USA. */ } while (0) #endif - -size_t -mpz_out_raw (FILE *fp, mpz_srcptr x) +void mpz_out_raw_m (mpir_out_ptr mpir_out, mpz_srcptr x) { mp_size_t xsize, abs_xsize, bytes, i; mp_srcptr xp; @@ -151,11 +149,25 @@ mpz_out_raw (FILE *fp, mpz_srcptr x) bp[-1] = bytes; bp -= 4; + mpir_out->allocated = tp; + mpir_out->allocatedSize = tsize; + mpir_out->written = bp; + mpir_out->writtenSize = ssize; +} + +size_t +mpz_out_raw (FILE* fp, mpz_srcptr x) +{ + mpir_out_struct out; + if (fp == 0) fp = stdout; - if (fwrite (bp, ssize, 1, fp) != 1) - ssize = 0; - (*__gmp_free_func) (tp, tsize); - return ssize; + mpz_out_raw_m(out, x); + + if (fwrite (out->written, out->writtenSize, 1, fp) != 1) + out->writtenSize = 0; + + (*__gmp_free_func) (out->allocated, out->allocatedSize); + return out->writtenSize; }