Skip to content

Commit cc84249

Browse files
committed
initial efforts (NOT ALL BUGS FIXED)
1 parent 9fdd4a1 commit cc84249

27 files changed

Lines changed: 624 additions & 400 deletions

JsonToolsNppPlugin/Forms/TreeViewer.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Drawing;
44
using System.Linq;
5+
using System.Numerics;
56
using System.Runtime.InteropServices;
67
using System.Text;
78
using System.Text.RegularExpressions;
@@ -1259,12 +1260,13 @@ public static bool LengthOfStringInRegexMode(JNode[] nodes, char delim, char quo
12591260
return false;
12601261
}
12611262
IComparable value = jnode.value;
1262-
if (value is long || value is double)
1263+
1264+
if (value is BigInteger || value is double)
12631265
{
12641266
// two equal numbers may have several valid representations
12651267
// we will try to find the length of the representation used in the document,
12661268
// but we will quit and use the JSON string length if our first attempt fails to find the length
1267-
double d = Convert.ToDouble(value);
1269+
double d = (double)value;
12681270
int nodepos = jnode.position;
12691271
if (documentText is null)
12701272
documentText = selectionEnd < 0

JsonToolsNppPlugin/JSONTools/Dson.cs

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Text;
4+
using System.Numerics;
5+
using Kbg.NppPluginNET.PluginInfrastructure;
46

57
namespace JSON_Tools.JSON_Tools
68
{
@@ -26,15 +28,32 @@ class Dson
2628

2729
public static readonly string[] ArrayPairDelims = new string[] { "and", "also" };
2830

29-
private static string FormatInteger(long val)
31+
private static string FormatInteger(BigInteger val)
3032
{
31-
if (val == long.MinValue)
32-
// converting to octal turns signed numbers into unsigned numbers
33-
// this can be reversed for every negative number except long.MinValue
34-
return "1000000000000000000000";
35-
if (val < 0)
36-
return "-" + Convert.ToString(-val, 8);
37-
return Convert.ToString(val, 8);
33+
if (val == 0)
34+
return "0";
35+
bool negative = val < 0;
36+
if (negative)
37+
val = -val;
38+
// build the string representation backwards and then reverse it
39+
var sb = new StringBuilder();
40+
while (val > 0)
41+
{
42+
val = BigInteger.DivRem(val, 8, out BigInteger rem);
43+
sb.Append((int)rem);
44+
}
45+
int sblenM1 = sb.Length - 1;
46+
string s;
47+
if (sblenM1 > 0)
48+
{
49+
var chars = new char[sblenM1 + 1];
50+
for (int ii = 0; ii <= sblenM1; ii--)
51+
chars[ii] = sb[sblenM1 - ii];
52+
s = new string(chars);
53+
}
54+
else
55+
s = sb.ToString();
56+
return negative ? "-" + s : s;
3857
}
3958

4059
/// <summary>
@@ -88,7 +107,7 @@ public static string Dump(JNode json)
88107
case Dtype.NULL:
89108
return "empty";
90109
case Dtype.INT:
91-
return FormatInteger((long)json.value);
110+
return FormatInteger((BigInteger)json.value);
92111
case Dtype.FLOAT:
93112
// floating point numbers are formatted
94113
// such that fractional part, exponent, and integer part are all octal
@@ -99,7 +118,7 @@ public static string Dump(JNode json)
99118
throw new DsonDumpException($"{valstr} is fake number, can't understand. So silly, wow");
100119
StringBuilder partSb = new StringBuilder();
101120
sb = new StringBuilder();
102-
long part;
121+
BigInteger part;
103122
foreach (char c in valstr)
104123
{
105124
if ('0' <= c && c <= '9' || c == '+' || c == '-')
@@ -108,7 +127,7 @@ public static string Dump(JNode json)
108127
}
109128
else
110129
{
111-
part = Convert.ToInt64(partSb.ToString());
130+
part = BigInteger.Parse(partSb.ToString());
112131
partSb = new StringBuilder();
113132
sb.Append(FormatInteger(part));
114133
if (c == '.')
@@ -119,7 +138,7 @@ public static string Dump(JNode json)
119138
sb.Append("VERY");
120139
}
121140
}
122-
part = Convert.ToInt64(partSb.ToString());
141+
part = BigInteger.Parse(partSb.ToString());
123142
sb.Append(FormatInteger(part));
124143
return sb.ToString();
125144
case Dtype.STR:

JsonToolsNppPlugin/JSONTools/JNode.cs

Lines changed: 122 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic; // for dictionary, list
77
using System.Globalization;
88
using System.Linq;
9+
using System.Numerics;
910
using System.Text;
1011
using System.Text.RegularExpressions;
1112

@@ -20,7 +21,7 @@ public enum Dtype : ushort
2021
TYPELESS = 0,
2122
/// <summary>represented as booleans</summary>
2223
BOOL = 1,
23-
/// <summary>represented as longs</summary>
24+
/// <summary>represented as <see cref="System.Numerics.BigInteger"/></summary>
2425
INT = 2,
2526
/// <summary>represented as doubles</summary>
2627
FLOAT = 4,
@@ -249,7 +250,7 @@ public JNode()
249250
//extras = null;
250251
}
251252

252-
public JNode(long value, int position = 0)
253+
public JNode(BigInteger value, int position = 0)
253254
{
254255
this.position = position;
255256
this.type = Dtype.INT;
@@ -403,6 +404,104 @@ public static void StrToSb(StringBuilder sb, string s)
403404
}
404405
}
405406

407+
public static BigInteger ConvertToBigInteger(object val)
408+
{
409+
if (val is BigInteger bi)
410+
return bi;
411+
if (val is double d)
412+
return (BigInteger)d;
413+
if (val is bool b)
414+
return b ? BigInteger.One : BigInteger.Zero;
415+
if (val is string s)
416+
return BigInteger.Parse(s);
417+
// hope that it's convertible to a long
418+
return (BigInteger)Convert.ToInt64(val);
419+
}
420+
421+
public static double ConvertJNodeValueToDouble(object val)
422+
{
423+
if (val is BigInteger bi)
424+
return (double)bi;
425+
if (val is double d)
426+
return d;
427+
if (val is bool b)
428+
return b ? 1D : 0D;
429+
// hope it's convertible to a double
430+
return Convert.ToDouble(val);
431+
}
432+
433+
/// <summary>
434+
/// return true and set d to (double)this.value if this.value is one of:<br></br>
435+
/// - a BigInteger that could be converted to a double.<br></br>
436+
/// - a double<br></br>
437+
/// - a bool (and set d = 1 if this.value else 0)<br></br>
438+
/// return false and set d to 0 otherwise.
439+
/// </summary>
440+
public bool TryGetValueAsDouble(out double d)
441+
{
442+
d = 0;
443+
if (value is double dval)
444+
{
445+
d = dval;
446+
return true;
447+
}
448+
if (value is bool b)
449+
{
450+
d = b ? 1D : 0D;
451+
return true;
452+
}
453+
if (value is BigInteger bi && bi >= DoubleMinValueAsBigInt && bi <= DoubleMaxValueAsBigInt)
454+
{
455+
d = (double)bi;
456+
return true;
457+
}
458+
return false;
459+
}
460+
461+
/// <summary>
462+
/// return true if this.value is one of:<br></br>
463+
/// - a BigInteger (and set bi to this.value)<br></br>
464+
/// - a bool (and set bi = 1 if this.value else 0)<br></br>
465+
/// return false and set bi to 0 otherwise.
466+
/// </summary>
467+
public bool TryGetValueAsBigInteger(out BigInteger bi)
468+
{
469+
bi = BigInteger.Zero;
470+
if (value is BigInteger biv)
471+
{
472+
bi = biv;
473+
return true;
474+
}
475+
if (value is bool b)
476+
{
477+
if (b)
478+
bi = BigInteger.One;
479+
return true;
480+
}
481+
return false;
482+
}
483+
484+
/// <summary>
485+
/// return true and set i to (int)this.value if this.value is a BigInteger that could be converted to an int.<br></br>
486+
/// return false and set i to 0 otherwise.
487+
/// </summary>
488+
/// <param name="d"></param>
489+
/// <returns></returns>
490+
public bool TryGetValueAsInt32(out int i)
491+
{
492+
i = 0;
493+
if (value is BigInteger bi && bi >= int.MinValue && bi <= int.MaxValue)
494+
{
495+
i = (int)bi;
496+
return true;
497+
}
498+
return false;
499+
}
500+
501+
public static readonly BigInteger DoubleMaxValueAsBigInt = (BigInteger)double.MaxValue;
502+
503+
public static readonly BigInteger DoubleMinValueAsBigInt = (BigInteger)double.MinValue;
504+
406505
/// <summary>
407506
/// Format a valid double as a string.<br></br>
408507
/// This method should not be used on NaN or number greater than <see cref="double.MaxValue"/> or less than <see cref="double.MinValue"/>,<br></br>
@@ -450,7 +549,7 @@ public virtual string ToString(bool sortKeys = true, string keyValueSep = ": ",
450549
if (double.IsNaN(v)) { return "NaN"; }
451550
return DoubleToString(v);
452551
}
453-
case Dtype.INT: return Convert.ToInt64(value).ToString();
552+
case Dtype.INT: return ((BigInteger)value).ToString();
454553
case Dtype.NULL: return "null";
455554
case Dtype.BOOL: return (bool)value ? "true" : "false";
456555
case Dtype.REGEX: return StrToString(((JRegex)this).regex.ToString(), true);
@@ -699,23 +798,33 @@ public int CompareTo(object other)
699798
// we could simply say value.CompareTo(other) after checking if value is null.
700799
// It is more user-friendly to attempt to allow comparison of different numeric types, so we do this instead
701800
case Dtype.STR: return ((string)value).CompareTo(other);
702-
case Dtype.INT: // Convert.ToInt64 has some weirdness where it rounds half-integers to the nearest even
801+
case Dtype.INT: // JNode.ConvertToBigInteger has some weirdness where it rounds half-integers to the nearest even
703802
// so it is generally preferable to have ints and floats compared the same way
704803
// this way e.g. "3.5 < 4" will be treated the same as "4 > 3.5",
705804
// which is the same comparison but with different operand order.
706-
// The only downside of this approach is that integers between
707-
// 4.5036e15 (2 ^ 52) and 9.2234e18 (2 ^ 63)
708-
// can be precisely represented by longs but not by doubles,
709-
// so very large integers will have a loss of precision.
805+
// Obviously if value and other are both too big to be coerced to doubles we will compare them as BigIntegers
806+
BigInteger bi = (BigInteger)value;
807+
if (other is BigInteger obi)
808+
return bi.CompareTo(obi);
809+
if (other is double od)
810+
{
811+
if (bi > DoubleMaxValueAsBigInt)
812+
return 1;
813+
if (bi < DoubleMinValueAsBigInt)
814+
return -1;
815+
return ((double)bi).CompareTo(od);
816+
}
817+
if (other is bool b)
818+
return bi.CompareTo(b ? BigInteger.One : BigInteger.Zero);
819+
throw new ArgumentException("Can't compare numbers to non-numbers");
710820
case Dtype.FLOAT:
711-
if (!(other is long || other is double || other is bool))
821+
if (!(other is BigInteger || other is double || other is bool))
712822
throw new ArgumentException("Can't compare numbers to non-numbers");
713-
return Convert.ToDouble(value).CompareTo(Convert.ToDouble(other));
823+
return ((double)value).CompareTo(ConvertJNodeValueToDouble(other));
714824
case Dtype.BOOL:
715-
if (!(other is long || other is double || other is bool))
825+
if (!(other is BigInteger || other is double || other is bool))
716826
throw new ArgumentException("Can't compare numbers to non-numbers");
717-
if ((bool)value) return (1.0).CompareTo(Convert.ToDouble(other));
718-
return (0.0).CompareTo(Convert.ToDouble(other));
827+
return ((bool)value ? 1.0 : 0.0).CompareTo(ConvertJNodeValueToDouble(other));
719828
case Dtype.NULL:
720829
if (other != null)
721830
throw new ArgumentException("Cannot compare null to non-null");

0 commit comments

Comments
 (0)