diff --git a/gmp-h.in b/gmp-h.in index 87c25ab7..c276f8d8 100644 --- a/gmp-h.in +++ b/gmp-h.in @@ -303,9 +303,9 @@ typedef __gmp_randstate_struct gmp_randstate_t[1]; /* Output of mp?_out_raw_m */ typedef struct { - unsigned char* allocated; + char* allocated; size_t allocatedSize; - unsigned char* written; + char* written; size_t writtenSize; } __mpir_out_struct; typedef __mpir_out_struct mpir_out_struct[1]; diff --git a/mpir.net/mpir.net-tests/HugeIntTests/IO.cs b/mpir.net/mpir.net-tests/HugeIntTests/IO.cs index ba1ca0f4..efb397b6 100644 --- a/mpir.net/mpir.net-tests/HugeIntTests/IO.cs +++ b/mpir.net/mpir.net-tests/HugeIntTests/IO.cs @@ -19,6 +19,7 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. using System; using System.IO; +using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace MPIR.Tests.HugeIntTests @@ -41,6 +42,149 @@ namespace MPIR.Tests.HugeIntTests Assert.AreEqual(ms.Length, ms.Position); } } + + [TestMethod] + public void InputOutputStr() + { + using (var a = new HugeInt("0x10123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")) + using (var b = new HugeInt()) + using (var ms = new MemoryStream()) + { + a.Value = a ^ 100; + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + a.Write(writer); + + ms.Position = 0; + + using (var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) + b.Read(reader); + + Assert.AreEqual(a, b); + Assert.AreEqual(ms.Length, ms.Position); + } + } + + [TestMethod] + public void InputOutputStrHex() + { + using (var a = new HugeInt("0x10123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")) + using (var b = new HugeInt()) + using (var ms = new MemoryStream()) + { + a.Value = a ^ 100; + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.Write("0x"); + a.Write(writer, 16); + } + + ms.Position = 0; + + using (var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) + b.Read(reader); + + Assert.AreEqual(a, b); + Assert.AreEqual(ms.Length, ms.Position); + Assert.AreEqual((char)0xFEFF + "0x" + a.ToString(16), Encoding.UTF8.GetString(ms.ToArray())); + } + } + + [TestMethod] + public void InputOutputStrHexLower() + { + using (var a = new HugeInt("0x10123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")) + using (var b = new HugeInt()) + using (var ms = new MemoryStream()) + { + a.Value = a ^ 100; + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.Write("0x"); + a.Write(writer, 16, true); + } + + ms.Position = 0; + + using (var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) + b.Read(reader); + + Assert.AreEqual(a, b); + Assert.AreEqual(ms.Length, ms.Position); + Assert.AreEqual((char)0xFEFF + "0x" + a.ToString(16, true), Encoding.UTF8.GetString(ms.ToArray())); + } + } + + [TestMethod] + public void InputOutputStrOctal() + { + using (var a = new HugeInt("0x10123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")) + using (var b = new HugeInt()) + using (var ms = new MemoryStream()) + { + a.Value = a ^ 1; + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.Write('0'); + a.Write(writer, 8); + } + + ms.Position = 0; + + using (var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) + b.Read(reader); + + Assert.AreEqual(a, b); + Assert.AreEqual(ms.Length, ms.Position); + Assert.AreEqual((char)0xFEFF + "0" + a.ToString(8), Encoding.UTF8.GetString(ms.ToArray())); + } + } + + [TestMethod] + public void InputOutputStrBinary() + { + using (var a = new HugeInt("0x10123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")) + using (var b = new HugeInt()) + using (var ms = new MemoryStream()) + { + a.Value = a ^ 100; + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + { + writer.Write("0b"); + a.Write(writer, 2); + } + + ms.Position = 0; + + using (var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) + b.Read(reader); + + Assert.AreEqual(a, b); + Assert.AreEqual(ms.Length, ms.Position); + Assert.AreEqual((char)0xFEFF + "0b" + a.ToString(2), Encoding.UTF8.GetString(ms.ToArray())); + } + } + + [TestMethod] + public void InputOutputStr62() + { + using (var a = new HugeInt("0x10123456789ABCDEF0123456789ABCDEF0123456789ABCDEF")) + using (var b = new HugeInt()) + using (var ms = new MemoryStream()) + { + a.Value = a ^ 100; + using (var writer = new StreamWriter(ms, Encoding.UTF8, 1024, true)) + a.Write(writer, 62); + + ms.Position = 0; + + using (var reader = new StreamReader(ms, Encoding.UTF8, false, 1024, true)) + b.Read(reader, 62); + + Assert.AreEqual(a, b); + Assert.AreEqual(ms.Length, ms.Position); + Assert.AreEqual((char)0xFEFF + a.ToString(62), Encoding.UTF8.GetString(ms.ToArray())); + } + } //more tests coming here } } diff --git a/mpir.net/mpir.net/Common.cpp b/mpir.net/mpir.net/Common.cpp index 7a6043ab..0e554108 100644 --- a/mpir.net/mpir.net/Common.cpp +++ b/mpir.net/mpir.net/Common.cpp @@ -18,29 +18,3 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. */ #include "Stdafx.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, size); -} - -void* CustomAllocate(size_t size) -{ - void* (*allocateFunc) (size_t); - mp_get_memory_functions(&allocateFunc, NULL, NULL); - return allocateFunc(size); -} - -void* CustomReallocate(void* old, size_t size) -{ - void* (*reallocateFunc) (void*, size_t, size_t); - mp_get_memory_functions(NULL, &reallocateFunc, NULL); - return reallocateFunc(old, 0, size); -} diff --git a/mpir.net/mpir.net/Common.h b/mpir.net/mpir.net/Common.h index 6c9944e8..dc01a34c 100644 --- a/mpir.net/mpir.net/Common.h +++ b/mpir.net/mpir.net/Common.h @@ -19,11 +19,6 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. #pragma once -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 { None = 0x0, diff --git a/mpir.net/mpir.net/HugeInt.cpp b/mpir.net/mpir.net/HugeInt.cpp index 4f431e29..73afa67b 100644 --- a/mpir.net/mpir.net/HugeInt.cpp +++ b/mpir.net/mpir.net/HugeInt.cpp @@ -89,6 +89,7 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. void dummy_ternary(mpz_ptr d, mpz_srcptr a, mpz_srcptr b, mpz_srcptr c) { }; using namespace System::Runtime::InteropServices; +using namespace System::Text; namespace MPIR { @@ -195,7 +196,7 @@ namespace MPIR auto result = gcnew System::Text::StringBuilder(); result->Append(sign); result->Append(gcnew String(str)); - CustomFree(str); + (*__gmp_free_func)(str, 0); return result->ToString(); } @@ -508,27 +509,30 @@ namespace MPIR #define chunkSize 1024 - void HugeInt::Write(Stream^ stream) + size_t HugeInt::Write(Stream^ stream) { mpir_out_struct out; mpz_out_raw_m(out, _value); auto buffer = gcnew array(chunkSize); auto ptr = out->written; + auto toWrite = (int)out->writtenSize; - while(out->writtenSize > 0) + while(toWrite > 0) { - auto len = Math::Min(chunkSize, (int)out->writtenSize); + auto len = Math::Min(chunkSize, toWrite); Marshal::Copy(IntPtr(ptr), buffer, 0, len); stream->Write(buffer, 0, len); ptr += len; - out->writtenSize -= len; + toWrite -= len; } - CustomFree(out->allocated, out->allocatedSize); + (*__gmp_free_func)(out->allocated, out->allocatedSize); + + return out->writtenSize; } - void HugeInt::Read(Stream^ stream) + size_t HugeInt::Read(Stream^ stream) { unsigned char csize_bytes[4]; mpir_out_struct out; @@ -566,5 +570,168 @@ namespace MPIR } } + size_t HugeInt::Write(TextWriter^ writer, int base, bool lowercase) + { + auto str = ToString(base, lowercase); + writer->Write(str); + return str->Length; + } + + #define X 0xff + const unsigned char digit_value_tab[] = + { + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, X, X, X, X, X, X, + X,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, + 25,26,27,28,29,30,31,32,33,34,35,X, X, X, X, X, + X,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, + 25,26,27,28,29,30,31,32,33,34,35,X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, X, X, X, X, X, X, + X,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24, + 25,26,27,28,29,30,31,32,33,34,35,X, X, X, X, X, + X,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50, + 51,52,53,54,55,56,57,58,59,60,61,X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, + X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X + }; + + size_t HugeInt::Read(TextReader^ reader, int base) + { + int c; + size_t nread = 0; + + /* Skip whitespace. */ + while ((c = reader->Peek()) >= 0 && Char::IsWhiteSpace(c)) + { + nread++; + reader->Read(); + } + + return ReadNoWhite(reader, base, nread); + } + +#define PEEK_NEXT_CHAR \ + reader->Read(); \ + c = reader->Peek(); \ + nread++; + + // adapted from inp_str, which is shared by mpq_inp_str + size_t HugeInt::ReadNoWhite(TextReader^ reader, int base, size_t nread) + { + char *str; + size_t alloc_size, str_size; + bool negative = false; + mp_size_t xsize; + const unsigned char* digit_value = digit_value_tab; + int c = reader->Peek(); + + if (base > 36) + { + // For bases > 36, use the collating sequence + // 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz + digit_value += 224; + if (base > 62) + throw gcnew ArgumentException("Invalid base", "base"); + } + + if (c == '-') + { + negative = true; + PEEK_NEXT_CHAR; + } + + if (c == EOF || digit_value[c] >= (base == 0 ? 10 : base)) + throw gcnew Exception("No digits found"); + + // If BASE is 0, try to find out the base by looking at the initial characters. + if (base == 0) + { + base = 10; + if (c == '0') + { + base = 8; + PEEK_NEXT_CHAR; + + switch(c = reader->Peek()) + { + case 'x': + case 'X': + base = 16; + PEEK_NEXT_CHAR; + break; + + case 'b': + case 'B': + base = 2; + PEEK_NEXT_CHAR; + } + } + } + + // Skip leading zeros + while (c == '0') + { + PEEK_NEXT_CHAR; + } + + alloc_size = 100; + str = (char *) (*__gmp_allocate_func) (alloc_size); + str_size = 0; + + while (c != EOF) + { + int dig = digit_value[c]; + if (dig >= base) + break; + + if (str_size >= alloc_size) + { + size_t old_alloc_size = alloc_size; + alloc_size = alloc_size * 3 / 2; + str = (char *) (*__gmp_reallocate_func) (str, old_alloc_size, alloc_size); + } + str[str_size++] = dig; + reader->Read(); + c = reader->Peek(); + } + nread += str_size; + + // Make sure the string is not empty, mpn_set_str would fail. + if (str_size == 0) + { + _value ->_mp_size = 0; + } + else + { + xsize = (((mp_size_t) + (str_size / __mp_bases[base].chars_per_bit_exactly)) + / GMP_NUMB_BITS + 2); + MPZ_REALLOC (_value, xsize); + + // Convert the byte array in base BASE to our bignum format. + xsize = mpn_set_str (_value->_mp_d, (unsigned char *) str, str_size, base); + _value->_mp_size = negative ? -xsize : xsize; + } + (*__gmp_free_func) (str, alloc_size); + return nread; + } + #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 638e92b7..7e0f45bf 100644 --- a/mpir.net/mpir.net/HugeInt.h +++ b/mpir.net/mpir.net/HugeInt.h @@ -1398,12 +1398,12 @@ namespace MPIR //construction void AllocateStruct() { - _value = (mpz_ptr)CustomAllocate(sizeof(__mpz_struct)); + _value = (mpz_ptr)((*__gmp_allocate_func)(sizeof(__mpz_struct))); } void DeallocateStruct() { mpz_clear(_value); - CustomFree(_value); + (*__gmp_free_func)(_value, 0); _value = nullptr; } void FromString(String^ value, int base); @@ -1743,27 +1743,6 @@ 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 #pragma region IO @@ -1777,7 +1756,8 @@ namespace MPIR /// for compatibility between 32-bit and 64-bit machines. /// /// Stream to output the number to - void Write(Stream^ stream); + /// the number of bytes written, or 0 if an error occurs. + size_t Write(Stream^ stream); /// /// Reads the integer value from the in raw binary format, as it would have been written by Write(Stream). @@ -1787,8 +1767,79 @@ namespace MPIR /// necessary for compatibility between 32-bit and 64-bit machines. /// /// Stream to input the number from - void Read(Stream^ stream); + /// the number of bytes read, or 0 if an error occurs. + size_t Read(Stream^ stream); + /// + /// Output the integer to the as a string of digits in decimal. + /// When writing multiple numbers that are to be read back with the Read(TextReader) method, + /// it is useful to separate the numbers with a character that is not a valid decimal digit. + /// This is because the Read method stops reading when it encounters a character that cannot represent a digit. + /// + /// Text writer to output the number to + /// the number of characters written + size_t Write(TextWriter^ writer) { return Write(writer, 0, false); } + + /// + /// Output the integer to the as a string of digits in base . + /// When writing multiple numbers that are to be read back with the Read(TextReader) method, + /// it is useful to separate the numbers with a character that is not a valid digit in base . + /// This is because the Read method stops reading when it encounters a character that cannot represent a digit. + /// For hexadecimal, binary, or octal, no leading base indication is written. + /// Therefore, for bases other than 10, use the Read(reader, base) overload rather than Read(reader) to read the number back. + /// + /// Text writer to output the number to + /// The base to use for the output. + /// The base can be from 2 to 62; uppercase letters represent digits 10-35 while lowercase letters represent digits 36-61. + /// the number of characters written + size_t Write(TextWriter^ writer, int base) { return Write(writer, base, false); } + + /// + /// Output the integer to the as a string of digits in base . + /// When writing multiple numbers that are to be read back with the Read(TextReader) method, + /// it is useful to separate the numbers with a character that is not a valid digit in base . + /// This is because the Read method stops reading when it encounters a character that cannot represent a digit. + /// For hexadecimal, binary, or octal, no leading base indication is written. + /// Therefore, for bases other than 10, use the Read(reader, base) overload rather than Read(reader) to read the number back. + /// + /// Text writer to output the number to + /// The base to use for the output. + /// The base can be from 2 to 62; Bases up to 36 use uppercase or lowercase letters based on the argument. + /// For bases larger than 36, the argument is ignored and uppercase letters represent digits 10-35 while lowercase letters represent digits 36-61. + /// Indicates if lowercase or uppercase letters should be used for the output. + /// This argument is ignored for bases larger than 36, where both uppercase and lowercase letters are used. + /// the number of characters written + size_t Write(TextWriter^ writer, int base, bool lowercase); + + /// + /// Input the number as a possibly white-space preceeded string. + /// The base of the number is determined from the leading characters: 0x or 0X for hexadecimal, 0b or 0B for binary, 0 for octal, decimal otherwise. + /// Reading terminates at end-of-stream, or up to but not including a character that is not a valid digit. + /// This method reads the output of a Write(TextWriter) when decimal base is used. + /// For hexadecimal, binary, or octal, because Write(TextWriter) doesn't write leading base indication characters, + /// using this overload of Read will fail to recognize the correct base. + /// Text reader to input the number from + /// the number of characters read + size_t Read(TextReader^ reader) { return Read(reader, 0); } + + /// + /// Input the number as a possibly white-space preceeded string in base from the . + /// Reading terminates at end-of-stream, or up to but not including a character that is not a valid digit. + /// This method reads the output of a Write(TextWriter) method. + /// + /// Text reader to input the number from + /// The base to use for the input. + /// The base can be from 2 to 62; For bases up to 36 case is ignored. + /// For bases larger than 36, uppercase letters represent digits 10-35 while lowercase letters represent digits 36-61. + /// If 0, the base of the number is determined from the leading characters: 0x or 0X for hexadecimal, 0b or 0B for binary, 0 for octal, decimal otherwise. + /// Note that the leading base characters are not written by the Write method. + /// the number of characters read + size_t Read(TextReader^ reader, int base); + + internal: + size_t ReadNoWhite(TextReader^ reader, int base, size_t nread); + + public: #pragma endregion }; }; \ No newline at end of file diff --git a/mpir.net/mpir.net/Stdafx.h b/mpir.net/mpir.net/Stdafx.h index a2887841..5398e387 100644 --- a/mpir.net/mpir.net/Stdafx.h +++ b/mpir.net/mpir.net/Stdafx.h @@ -24,5 +24,6 @@ along with the MPIR Library. If not, see http://www.gnu.org/licenses/. #pragma once #include "mpir.h" +#include "gmp-impl.h" #include "Common.h" #include "HugeInt.h" diff --git a/mpz/inp_raw.c b/mpz/inp_raw.c index 472770e2..aca99263 100644 --- a/mpz/inp_raw.c +++ b/mpz/inp_raw.c @@ -107,7 +107,7 @@ void mpz_inp_raw_p (mpz_ptr x, unsigned char* csize_bytes, mpir_out_ptr out) /* Get limb boundaries right in the read, for the benefit of the non-nails case. */ xp[0] = 0; - out->written = (unsigned char *) (xp + abs_xsize) - abs_csize; + out->written = (char *) (xp + abs_xsize) - abs_csize; } out->writtenSize = abs_csize; out->allocatedSize = abs_xsize;