'From Squeak3.10.1-basic of 13 May 2008 [latest update: #7164] on 20 May 2008 at 7:10:53 am'! "Change Set: SqNumberParser_speedUp_M6976_nice Date: 10 March 2008 Author: nice This changeSet makes SqNumberParser about twice and up to four times faster than it used to in Float Number crunching. This is http://bugs.squeak.org/view.php?id=6976. The four improvements are: 1) Inline digitValue when radix <= 10. This is the main gain. 2) Don't test 'NaN' and 'Infinity' in preamble. Test only in exceptional conditions (when no digit were found). 3) hack (aRadix raisedTo: exponent) with base 2 bitShift: arithmetic and use timesTwoPower: a posteriori to avoid some LargePositiveInteger. 4) hack PositionableStream>>position: replace & condition by and: which is inlined by Compiler"! !PositionableStream methodsFor: 'positioning' stamp: 'nice 3/10/2008 22:29'! position: anInteger "Set the current position for accessing the objects to be anInteger, as long as anInteger is within the bounds of the receiver's contents. If it is not, create an error notification." (anInteger >= 0 and: [anInteger <= readLimit]) ifTrue: [position := anInteger] ifFalse: [self positionError]! ! !SqNumberParser methodsFor: 'parsing-private' stamp: 'nice 3/10/2008 23:13'! makeFloatFromMantissa: m exponent: k base: aRadix "Convert infinite precision arithmetic into Floating point. This alogrithm rely on correct IEEE rounding mode being implemented in Integer>>asFloat and Fraction>>asFloat" | p | p := aRadix lowBit - 1. ^k positive ifTrue: [(m * (((aRadix bitShift: p negated) raisedToInteger: k))) asFloat timesTwoPower: p*k] ifFalse: [(Fraction numerator: m denominator: (((aRadix bitShift: p negated) raisedToInteger: k negated) bitShift: p*k negated)) asFloat]! ! !SqNumberParser methodsFor: 'parsing-public' stamp: 'nice 3/10/2008 22:50'! nextNumber "main method for reading a number. This one can read Float Integer and ScaledDecimal" | numberOfTrailingZeroInIntegerPart numberOfNonZeroFractionDigits mantissa decimalMultiplier decimalFraction value numberOfTrailingZeroInFractionPart | neg := sourceStream peekFor: $-. integerPart := self nextUnsignedIntegerBase: base ifFail: [ "This is not a regular number beginning with a digit It is time to check for exceptional condition NaN and Infinity" neg ifFalse: [(sourceStream nextMatchAll: 'NaN') ifTrue: [^ Float nan]]. (sourceStream nextMatchAll: 'Infinity') ifTrue: [^ neg ifTrue: [Float infinity negated] ifFalse: [Float infinity]]. ^self expected: ['a digit between 0 and 9']]. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero. (sourceStream peekFor: $r) ifTrue: ["r" (base := integerPart) < 2 ifTrue: [^ self expected: 'an integer greater than 1 as valid radix']. (sourceStream peekFor: $-) ifTrue: [neg := neg not]. integerPart := self nextUnsignedIntegerBase: base. numberOfTrailingZeroInIntegerPart := nDigits - lastNonZero]. ^ (sourceStream peekFor: $.) ifTrue: [fractionPart := self nextUnsignedIntegerBase: base ifFail: [sourceStream skip: -1. ^ neg ifTrue: [integerPart negated] ifFalse: [integerPart]]. numberOfNonZeroFractionDigits := lastNonZero. numberOfTrailingZeroInFractionPart := nDigits - lastNonZero. self readExponent ifFalse: [self readScale ifTrue: [decimalMultiplier := base raisedTo: numberOfNonZeroFractionDigits. decimalFraction := integerPart * decimalMultiplier + fractionPart / decimalMultiplier. neg ifTrue: [decimalFraction := decimalFraction negated]. ^ ScaledDecimal newFromNumber: decimalFraction scale: scale]]. fractionPart isZero ifTrue: [mantissa := integerPart // (base raisedTo: numberOfTrailingZeroInIntegerPart). exponent := exponent + numberOfTrailingZeroInIntegerPart] ifFalse: [mantissa := integerPart * (base raisedTo: numberOfNonZeroFractionDigits) + (fractionPart // (base raisedTo: numberOfTrailingZeroInFractionPart)). exponent := exponent - numberOfNonZeroFractionDigits]. "very naive algorithm" value := self makeFloatFromMantissa: mantissa exponent: exponent base: base. ^ neg ifTrue: [value isZero ifTrue: [Float negativeZero] ifFalse: [value negated]] ifFalse: [value]] ifFalse: [self makeIntegerOrScaledInteger]! ! !SqNumberParser methodsFor: 'parsing-public' stamp: 'nice 3/10/2008 22:44'! nextUnsignedIntegerBase: aRadix "Form an unsigned integer with incoming digits from sourceStream. Count the number of digits and the lastNonZero digit and store int in instVar " ^ self nextUnsignedIntegerBase: aRadix ifFail: [self expected: ('a digit between 0 and ' copyWith: (Character digitValue: aRadix - 1))]! ! !SqNumberParser methodsFor: 'parsing-public' stamp: 'nice 3/10/2008 22:27'! nextUnsignedIntegerBase: aRadix ifFail: errorBlock "Form an unsigned integer with incoming digits from sourceStream. Count the number of digits and the lastNonZero digit and store int in instVar" | value digit | value := 0. nDigits := 0. lastNonZero := 0. aRadix <= 10 ifTrue: ["Avoid using digitValue which is awfully slow" [sourceStream atEnd or: [digit := sourceStream next charCode - 48. (digit < 0 or: [digit >= aRadix]) and: [sourceStream skip: -1. true]]] whileFalse: [nDigits := nDigits + 1. digit isZero ifFalse: [lastNonZero := nDigits]. value := value * aRadix + digit]] ifFalse: [ [sourceStream atEnd or: [digit := sourceStream next digitValue. (digit < 0 or: [digit >= aRadix]) and: [sourceStream skip: -1. true]]] whileFalse: [nDigits := nDigits + 1. digit isZero ifFalse: [lastNonZero := nDigits]. value := value * aRadix + digit]]. nDigits = 0 ifTrue: [errorBlock value]. ^value! !