@@ -21,6 +21,7 @@ var LobOptions = common.LobOptions;
21
21
var NormalizedTypeCode = common . NormalizedTypeCode ;
22
22
var bignum = util . bignum ;
23
23
var calendar = util . calendar ;
24
+ var zeropad = require ( '../util/zeropad' ) ;
24
25
var isValidDay = calendar . isValidDay ;
25
26
var isValidTime = calendar . isValidTime ;
26
27
var isZeroDay = calendar . isZeroDay ;
@@ -39,12 +40,14 @@ var REGEX = {
39
40
} ;
40
41
41
42
const maxDecimalMantissaLen = 34 ;
43
+ const maxFixedMantissaLen = 38 ;
42
44
43
- function Writer ( types , useCesu8 , spatialTypes ) {
44
- this . _types = types . map ( normalizeType ) ;
45
+ function Writer ( params , options ) {
46
+ this . _types = params . types . map ( normalizeType ) ;
47
+ this . _fractions = params . fractions ;
45
48
this . reset ( ) ;
46
- this . _useCesu8 = ( useCesu8 === true ) ;
47
- this . _spatialTypes = ( spatialTypes === 1 ? 1 : 0 ) ;
49
+ this . _useCesu8 = ( options && options . useCesu8 === true ) ;
50
+ this . _spatialTypes = ( ( options && options . spatialTypes === 1 ) ? 1 : 0 ) ;
48
51
}
49
52
50
53
function normalizeType ( type ) {
@@ -66,13 +69,13 @@ Writer.prototype.reset = function reset() {
66
69
Writer . prototype . setValues = function setValues ( values ) {
67
70
this . reset ( ) ;
68
71
for ( var i = 0 ; i < values . length ; i ++ ) {
69
- this . add ( this . _types [ i ] , values [ i ] ) ;
72
+ this . add ( this . _types [ i ] , values [ i ] , this . _fractions ? this . _fractions [ i ] : undefined ) ;
70
73
}
71
74
this . _params = true ;
72
75
} ;
73
76
74
- exports . create = function createWriter ( params , useCesu8 , spatialTypes ) {
75
- var writer = new Writer ( params . types , useCesu8 , spatialTypes ) ;
77
+ exports . create = function createWriter ( params , options ) {
78
+ var writer = new Writer ( params , options ) ;
76
79
writer . setValues ( params . values ) ;
77
80
return writer ;
78
81
} ;
@@ -95,9 +98,11 @@ Object.defineProperties(Writer.prototype, {
95
98
}
96
99
} ) ;
97
100
98
- Writer . prototype . add = function add ( type , value ) {
101
+ Writer . prototype . add = function add ( type , value , fraction ) {
99
102
if ( typeof value === 'undefined' || value === null ) {
100
103
this . pushNull ( type ) ;
104
+ } else if ( type === TypeCode . FIXED8 || type === TypeCode . FIXED12 || type === TypeCode . FIXED16 ) {
105
+ this [ type ] ( value , fraction ) ;
101
106
} else {
102
107
this [ type ] ( value ) ;
103
108
}
@@ -491,9 +496,9 @@ Writer.prototype[TypeCode.DOUBLE] = function writeDouble(value) {
491
496
Writer . prototype [ TypeCode . DECIMAL ] = function writeDecimal ( value ) {
492
497
var decimal ;
493
498
if ( util . isString ( value ) ) {
494
- decimal = stringToDecimal ( value ) ;
499
+ decimal = stringToDecimal ( value , maxDecimalMantissaLen ) ;
495
500
} else if ( util . isNumber ( value ) ) {
496
- decimal = stringToDecimal ( value . toExponential ( ) ) ;
501
+ decimal = stringToDecimal ( value . toExponential ( ) , maxDecimalMantissaLen ) ;
497
502
} else {
498
503
throw createInputError ( 'DECIMAL' ) ;
499
504
}
@@ -503,6 +508,100 @@ Writer.prototype[TypeCode.DECIMAL] = function writeDecimal(value) {
503
508
this . push ( buffer ) ;
504
509
} ;
505
510
511
+ function fixedToDecimal ( value , fraction , typeStr ) {
512
+ var decimal ;
513
+ // Convert to decimal object with maximum number of digits 38 and minimum exponent is
514
+ // -fraction, so there are at most 'fraction' digits after the decimal
515
+ if ( util . isString ( value ) ) {
516
+ decimal = stringToDecimal ( value , maxFixedMantissaLen , - fraction , typeStr ) ;
517
+ } else if ( util . isNumber ( value ) ) {
518
+ decimal = stringToDecimal ( value . toExponential ( ) , maxFixedMantissaLen , - fraction , typeStr ) ;
519
+ } else {
520
+ throw createInputError ( typeStr ) ;
521
+ }
522
+ if ( decimal . m . length + decimal . e + fraction > maxFixedMantissaLen ) {
523
+ throw createInputError ( typeStr ) ; // Numeric overflow, greater than maximum precision
524
+ }
525
+
526
+ if ( ( - decimal . e ) < fraction ) {
527
+ decimal . m += zeropad . ZEROS [ fraction + decimal . e ] ;
528
+ }
529
+ return decimal ;
530
+ }
531
+
532
+ function writeFixed16Buffer ( decimal , buffer , offset ) {
533
+ bignum . writeUInt128LE ( buffer , decimal . m , offset ) ;
534
+ if ( decimal . s === - 1 ) {
535
+ // Apply two's complement conversion
536
+ var extraOne = true ;
537
+ for ( var i = offset ; i < offset + 16 ; i ++ ) {
538
+ if ( extraOne ) {
539
+ if ( buffer [ i ] !== 0 ) {
540
+ buffer [ i ] = 0xff - buffer [ i ] + 1 ;
541
+ extraOne = false ;
542
+ } else {
543
+ buffer [ i ] = 0 ;
544
+ }
545
+ } else {
546
+ buffer [ i ] = 0xff - buffer [ i ] ;
547
+ }
548
+ }
549
+ }
550
+ }
551
+
552
+ function checkFixedOverflow ( decimal , extBuffer , byteLimit , typeStr ) {
553
+ if ( decimal . s === - 1 ) {
554
+ for ( var i = byteLimit ; i < 16 ; ++ i ) {
555
+ if ( extBuffer [ i ] != 0xff ) {
556
+ throw createInputError ( typeStr ) ;
557
+ }
558
+ }
559
+ if ( ( extBuffer [ byteLimit - 1 ] & 0x80 ) == 0 ) {
560
+ throw createInputError ( typeStr ) ;
561
+ }
562
+ } else {
563
+ for ( var i = byteLimit ; i < 16 ; ++ i ) {
564
+ if ( extBuffer [ i ] != 0 ) {
565
+ throw createInputError ( typeStr ) ;
566
+ }
567
+ }
568
+ if ( extBuffer [ byteLimit - 1 ] & 0x80 ) {
569
+ throw createInputError ( typeStr ) ;
570
+ }
571
+ }
572
+ }
573
+
574
+ Writer . prototype [ TypeCode . FIXED8 ] = function writeFixed8 ( value , fraction ) {
575
+ var extBuffer = new Buffer ( 16 ) ;
576
+ var decimal = fixedToDecimal ( value , fraction , 'FIXED8' ) ;
577
+ writeFixed16Buffer ( decimal , extBuffer , 0 ) ;
578
+ // Check that the representation does not exceed 8 bytes
579
+ checkFixedOverflow ( decimal , extBuffer , 8 , 'FIXED8' ) ;
580
+ var buffer = new Buffer ( 9 ) ;
581
+ buffer [ 0 ] = TypeCode . FIXED8 ;
582
+ extBuffer . copy ( buffer , 1 , 0 , 8 ) ;
583
+ this . push ( buffer ) ;
584
+ }
585
+
586
+ Writer . prototype [ TypeCode . FIXED12 ] = function writeFixed12 ( value , fraction ) {
587
+ var extBuffer = new Buffer ( 16 ) ;
588
+ var decimal = fixedToDecimal ( value , fraction , 'FIXED12' ) ;
589
+ writeFixed16Buffer ( decimal , extBuffer , 0 ) ;
590
+ // Check that the representation does not exceed 12 bytes
591
+ checkFixedOverflow ( decimal , extBuffer , 12 , 'FIXED12' ) ;
592
+ var buffer = new Buffer ( 13 ) ;
593
+ buffer [ 0 ] = TypeCode . FIXED12 ;
594
+ extBuffer . copy ( buffer , 1 , 0 , 12 ) ;
595
+ this . push ( buffer ) ;
596
+ }
597
+
598
+ Writer . prototype [ TypeCode . FIXED16 ] = function writeFixed16 ( value , fraction ) {
599
+ var buffer = new Buffer ( 17 ) ;
600
+ buffer [ 0 ] = TypeCode . FIXED16 ;
601
+ writeFixed16Buffer ( fixedToDecimal ( value , fraction , 'FIXED16' ) , buffer , 1 ) ;
602
+ this . push ( buffer ) ;
603
+ }
604
+
506
605
Writer . prototype [ TypeCode . NSTRING ] = function writeNString ( value ) {
507
606
this . writeCharacters ( TypeCode . NSTRING , value ) ;
508
607
} ;
@@ -825,12 +924,12 @@ function trimTrailingZeroes(str) {
825
924
return str . substring ( 0 , i + 1 ) ;
826
925
}
827
926
828
- function stringToDecimal ( str ) {
927
+ function stringToDecimal ( str , maxMantissaLen , minExp , typeStr ) {
829
928
/* jshint bitwise:false */
830
929
var dec = str . match ( REGEX . DECIMAL ) ;
831
930
// REGEX.DECIMAL will match "." and "" despite these being invalid.
832
931
if ( ! dec || str === "." || str === "" ) {
833
- throw createInputError ( 'DECIMAL' ) ;
932
+ throw createInputError ( typeStr === undefined ? 'DECIMAL' : typeStr ) ;
834
933
}
835
934
var sign = dec [ 1 ] === '-' ? - 1 : 1 ;
836
935
var mInt = dec [ 2 ] || '' ;
@@ -842,14 +941,27 @@ function stringToDecimal(str) {
842
941
if ( mantissa . length === 0 ) mantissa = "0" ;
843
942
exp -= mFrac . length
844
943
845
- // round to maxDecimalMantissaLen digits and increment exp appropriately
846
- if ( mantissa . length > maxDecimalMantissaLen ) {
847
- var followDigit = mantissa [ maxDecimalMantissaLen ] ;
848
- exp += ( mantissa . length - maxDecimalMantissaLen )
849
- mantissa = mantissa . substring ( 0 , maxDecimalMantissaLen ) ;
944
+ var calcMaxMantissaLength = maxMantissaLen ;
945
+ if ( minExp !== undefined && exp < minExp ) {
946
+ // Shift the maxMantissaLen such that the exponent is minExp
947
+ calcMaxMantissaLength = exp + mantissa . length - minExp ;
948
+ if ( calcMaxMantissaLength < 0 ) {
949
+ // All digits are rounded away
950
+ return { s : 1 , m : "0" , e : 0 } ;
951
+ } else if ( calcMaxMantissaLength == 0 ) {
952
+ // Only the first digit matters
953
+ return { s : sign , m : mantissa [ 0 ] > '4' ? "1" : "0" , e : minExp } ;
954
+ }
955
+ }
956
+
957
+ // round to calcMaxMantissaLen digits and increment exp appropriately
958
+ if ( mantissa . length > calcMaxMantissaLength ) {
959
+ var followDigit = mantissa [ calcMaxMantissaLength ] ;
960
+ exp += ( mantissa . length - calcMaxMantissaLength ) ;
961
+ mantissa = mantissa . substring ( 0 , calcMaxMantissaLength ) ;
850
962
if ( followDigit > '4' ) {
851
963
// round up
852
- var i = maxDecimalMantissaLen - 1 ;
964
+ var i = calcMaxMantissaLength - 1 ;
853
965
while ( i >= 0 && mantissa [ i ] === '9' ) {
854
966
i -= 1 ;
855
967
}
@@ -862,9 +974,9 @@ function stringToDecimal(str) {
862
974
mantissa = mantissa . substring ( 0 , i + 1 ) ;
863
975
mantissa = setChar ( mantissa , i , String . fromCharCode ( mantissa . charCodeAt ( i ) + 1 ) ) ;
864
976
}
865
- } else if ( mantissa [ maxDecimalMantissaLen - 1 ] === '0' ) {
977
+ } else if ( mantissa [ calcMaxMantissaLength - 1 ] === '0' ) {
866
978
var trimmed = trimTrailingZeroes ( mantissa ) ;
867
- exp += ( maxDecimalMantissaLen - trimmed . length ) ;
979
+ exp += ( calcMaxMantissaLength - trimmed . length ) ;
868
980
mantissa = trimmed ;
869
981
}
870
982
}
0 commit comments