Skip to content

Commit

Permalink
Merge pull request #83 from boguslavsky/floats
Browse files Browse the repository at this point in the history
add floating types support
  • Loading branch information
boguslavsky authored Mar 6, 2018
2 parents d740589 + e3459db commit 89c7b38
Show file tree
Hide file tree
Showing 10 changed files with 236 additions and 5 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ These types must be used when constructing custom data types.
| **Uint16** | 2 | Number in a range from `0` to `65535`. | `Number` |
| **Uint32** | 4 | Number in a range from `0` to `4294967295`. | `Number` |
| **Uint64** | 8 | Number in a range from `0` to `18446744073709551615`. | `Number` or `String`\* |
| **Float32** | 4 | Floating point number in a range from `-3.40282347e+38f32` to `3.40282347e+38f32`. | `Number` or `String`\* |
| **Float64** | 8 | Floating point number in a range from `-1.7976931348623157e+308f64` to `1.7976931348623157e+308f64`. | `Number` or `String`\* |
| **String** | 8\*\* | A string of variable length consisting of UTF-8 characters. | `String` |
| **Hash** | 32 | Hexadecimal string. | `String` |
| **PublicKey** | 32 | Hexadecimal string. | `String` |
Expand Down
5 changes: 2 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "exonum-client",
"version": "0.4.1",
"version": "0.5.0",
"description": "Light Client for Exonum Blockchain",
"main": "./lib/index.js",
"engines": {
Expand All @@ -16,6 +16,7 @@
],
"dependencies": {
"big-integer": "^1.6.17",
"ieee754": "^1.1.8",
"sha.js": "^2.4.8",
"tweetnacl": "^0.14.5"
},
Expand Down
46 changes: 46 additions & 0 deletions src/types/primitive.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import bigInt from 'big-integer'
import * as ieee754 from 'ieee754'
import * as validate from './validate'

const MIN_INT8 = -128
Expand Down Expand Up @@ -52,6 +53,21 @@ function insertIntegerToByteArray (number, buffer, from, to) {
return buffer
}

/**
* Insert IEEE754 floating point number into array as as little-endian
* @param {number} number
* @param {Array} buffer
* @param {number} from
* @param {number} mLen - mantissa length
* @param {number} nBytes - number of bytes
* @returns {boolean}
*/
function insertFloatToByteArray (number, buffer, from, mLen, nBytes) {
ieee754.write(buffer, number, from, true, mLen, nBytes)

return buffer
}

/**
* @param {string} str
* @param {Array} buffer
Expand Down Expand Up @@ -221,6 +237,36 @@ export function Uint64 (value, buffer, from, to) {
return insertIntegerToByteArray(val, buffer, from, to)
}

/**
* @param {number} value
* @param {Array} buffer
* @param {number} from
* @param {number} to
* @returns {Array}
*/
export function Float32 (value, buffer, from, to) {
if (!validate.validateFloat(value, from, to, 4)) {
throw new TypeError('Float32 of wrong type is passed: ' + value)
}

return insertFloatToByteArray(value, buffer, from, 23, 4)
}

/**
* @param {number} value
* @param {Array} buffer
* @param {number} from
* @param {number} to
* @returns {Array}
*/
export function Float64 (value, buffer, from, to) {
if (!validate.validateFloat(value, from, to, 8)) {
throw new TypeError('Float64 of wrong type is passed: ' + value)
}

return insertFloatToByteArray(value, buffer, from, 52, 8)
}

/**
* @param {string} string
* @param {Array} buffer
Expand Down
19 changes: 19 additions & 0 deletions src/types/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ export function validateBigInteger (value, min, max, from, to, length) {
return true
}

/**
* @param {number} value
* @param {number} from
* @param {number} to
* @param {number} length
* @returns {boolean}
*/
export function validateFloat (value, from, to, length) {
if (typeof value !== 'number' && typeof value !== 'string') {
// wrong type
return false
} else if ((to - from) !== length) {
// segment is of wrong length
return false
}

return true
}

/**
* @param {string} hash
* @param {number} [bytes=32] - optional
Expand Down
22 changes: 22 additions & 0 deletions test/common_data/serialization/types-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,27 @@
"to": 8
}
}
},
"type16": {
"size": 4,
"fields": {
"balance": {
"type": "Float32",
"size": 4,
"from": 0,
"to": 4
}
}
},
"type17": {
"size": 8,
"fields": {
"balance": {
"type": "Float64",
"size": 8,
"from": 0,
"to": 8
}
}
}
}
20 changes: 20 additions & 0 deletions test/common_data/serialization/types-invalid.json
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,25 @@
"data": {
"balance": "18446744073709551616"
}
},
"type16": {
"data": {
"balance": 1234.5678
}
},
"type16-1": {
"data": {
"balance": -1234.5678
}
},
"type17": {
"data": {
"balance": 9223372036854775808
}
},
"type17-1": {
"data": {
"balance": -9223372036854775809
}
}
}
26 changes: 26 additions & 0 deletions test/common_data/serialization/types.json
Original file line number Diff line number Diff line change
Expand Up @@ -364,5 +364,31 @@
241,
90
]
},
"type16": {
"data": {
"balance": 3.14
},
"serialized": [
195,
245,
72,
64
]
},
"type17": {
"data": {
"balance": 1234567.890
},
"serialized": [
61,
10,
215,
227,
135,
214,
50,
65
]
}
}
2 changes: 1 addition & 1 deletion test/data_schema/dataSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export default class DataSchema {
this.messages = {}

const ExonumTypes = ['PublicKey', 'String', 'Hash', 'Digest', 'Timespec',
'Bool', 'Int8', 'Int16', 'Int32', 'Int64', 'Uint8', 'Uint16', 'Uint32', 'Uint64']
'Bool', 'Int8', 'Int16', 'Int32', 'Int64', 'Uint8', 'Uint16', 'Uint32', 'Uint64', 'Float32', 'Float64']
ExonumTypes.forEach(item => (this.types[item] = Exonum[item]))

Object.keys(schema).forEach(key => {
Expand Down
96 changes: 96 additions & 0 deletions test/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,102 @@ describe('Check built-in types', function () {
})
})

describe('Process Float32', function () {
it('should serialize data and return array of 8-bit integers when the value is valid positive number', function () {
const buffer = scheme.getType('type16').serialize(typesMock.type16.data)
expect(buffer).to.deep.equal(typesMock.type16.serialized)
})

it('should serialize data and return array of 8-bit integers when the value is valid positive number as string', function () {
const data = { balance: '340282350000000000000000000000000000000.0' }
const buffer = scheme.getType('type16').serialize(data)

expect(buffer).to.deep.equal([255, 255, 127, 127])
})

it('should serialize data and return array of 8-bit integers when the value is valid negative number', function () {
const data = { balance: -1234.567 }
const buffer = scheme.getType('type16').serialize(data)

expect(buffer).to.deep.equal([37, 82, 154, 196])
})

it('should serialize data and return array of 8-bit integers when the value is valid negative number as string', function () {
const data = { balance: '-340282350000000000000000000000000000000.0' }
const buffer = scheme.getType('type16').serialize(data)

expect(buffer).to.deep.equal([255, 255, 127, 255])
})

it('should throw error when the range of segment is invalid', function () {
const Type = Exonum.newType({
size: 4,
fields: { balance: { type: Exonum.Float32, size: 4, from: 0, to: 3 } }
})

expect(() => Type.serialize({ balance: 3.14 }))
.to.throw(TypeError, 'Float32 of wrong type is passed: 3.14')
})

it('should throw error when the value of invalid type', function () {
expect(() => scheme.getType('type16').serialize({ balance: undefined }))
.to.throw(TypeError, 'Field balance is not defined.');

[true, null, [], {}, new Date()].forEach(function (balance) {
expect(() => scheme.getType('type16').serialize({ balance: balance }))
.to.throw(TypeError, /Float32 of wrong type is passed:/)
})
})
})

describe('Process Float64', function () {
it('should serialize data and return array of 8-bit integers when the value is valid positive number', function () {
const buffer = scheme.getType('type17').serialize(typesMock.type17.data)
expect(buffer).to.deep.equal(typesMock.type17.serialized)
})

it('should serialize data and return array of 8-bit integers when the value is valid positive number as string', function () {
const data = { balance: '179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0' }
const buffer = scheme.getType('type17').serialize(data)

expect(buffer).to.deep.equal([255, 255, 255, 255, 255, 255, 239, 127])
})

it('should serialize data and return array of 8-bit integers when the value is valid negative number', function () {
const data = { balance: -98765.4321 }
const buffer = scheme.getType('type17').serialize(data)

expect(buffer).to.deep.equal([138, 176, 225, 233, 214, 28, 248, 192])
})

it('should serialize data and return array of 8-bit integers when the value is valid negative number as string', function () {
const data = { balance: '-179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0' }
const buffer = scheme.getType('type17').serialize(data)

expect(buffer).to.deep.equal([255, 255, 255, 255, 255, 255, 239, 255])
})

it('should throw error when the range of segment is invalid', function () {
const Type = Exonum.newType({
size: 8,
fields: { balance: { type: Exonum.Float64, size: 8, from: 0, to: 4 } }
})

expect(() => Type.serialize({ balance: 613.228 }))
.to.throw(TypeError, 'Float64 of wrong type is passed: 613.228')
})

it('should throw error when the value of invalid type', function () {
expect(() => scheme.getType('type17').serialize({ balance: undefined }))
.to.throw(TypeError, 'Field balance is not defined.');

[true, null, [], {}, new Date()].forEach(function (balance) {
expect(() => scheme.getType('type17').serialize({ balance: balance }))
.to.throw(TypeError, /Float64 of wrong type is passed:/)
})
})
})

describe('Process Array', function () {
it('should serialize valid array of Hash type and return array of 8-bit integers', function () {
let buffer = scheme.getType('type15-2').serialize(typesMock.type15.data)
Expand Down

0 comments on commit 89c7b38

Please sign in to comment.