Skip to content

Commit

Permalink
Heterogeneous spread integers, top bits.
Browse files Browse the repository at this point in the history
Set top bits of spread integers. Noeed to implement hetergeneous
integers now for MQTT. Going to pause before implementing
best-foot-forward calculations to add the spread and upper properties
to the AST.

See #572.
See #571.
  • Loading branch information
flatheadmill committed Jul 29, 2020
1 parent 4b961a5 commit 65d4cc5
Show file tree
Hide file tree
Showing 27 changed files with 807 additions and 27 deletions.
31 changes: 23 additions & 8 deletions language.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ module.exports = function (packets) {
if (
Array.isArray(packet) &&
packet.every(number => Number.isInteger(number)) &&
packet.slice(1).every(number => number > 0)
packet.slice(1).every(number => number >= 0)
) {
const spread = packet.slice(1)
const abs = Math.abs(packet[0] % 8)
Expand All @@ -165,9 +165,15 @@ module.exports = function (packets) {
? Math.abs(~packet[0])
: -~packet[0]
: Math.abs(packet[0])
return bits > packet.slice(1).reduce((sum, number) => {
return sum + number
}, 0)
const bytes = bits / 8
if (bytes == spread.length) {
return bits > packet.slice(1)
.reduce((sum, number) => sum + number, 0)
} else if (bytes == spread.length / 2) {
return bits > packet.slice(1)
.filter((number, index) => index % 2 == 1)
.reduce((sum, number) => sum + number, 0)
}
}
return false
},
Expand Down Expand Up @@ -475,10 +481,19 @@ module.exports = function (packets) {
// **Spread**: An integer spread out across multiple bytes that may or
// may not use every bit in that byte.
function spread () {
const spread = packet.slice(1)
if (Math.abs(packet[0] % 8) == 0) {
return [ integer(packet[0], false, { ...extra, spread }) ]
}
const abs = Math.abs(packet[0] % 8)
const bits = abs != 0
? abs == 1
? Math.abs(~packet[0])
: -~packet[0]
: Math.abs(packet[0])
const { spread, upper } = bits / 8 * 2 == packet.length - 1
? {
spread: packet.slice(1).filter((_, index) => index % 2 == 1),
upper: packet.slice(1).filter((_, index) => index % 2 == 0)
}
: { spread: packet.slice(1), upper: null }
return [ integer(packet[0], false, { ...extra, spread, upper }) ]
}
//

Expand Down
31 changes: 23 additions & 8 deletions parse.all.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,18 +427,33 @@ function generate (packet, { require, bff, chk }) {
}
return spread
} ()
const shifts = spread.map((bits, index) => {
return spread.slice(index + 1).reduce((sum, number) => sum + number, 0)
const upper = function () {
if (field.upper != null) {
return field.upper
}
return spread.map(() => 0)
} ()
const combined = spread.map((bits, index) => {
return {
mask: 0xff >>> 8 - bits,
shift: BigInt(spread.slice(index + 1).reduce((sum, number) => sum + number, 0)),
upper: upper[index]
}
})
if (field.endianness == 'little') {
shifts.reverse()
if (field.endianness == 'big') {
combined.reverse()
}
const reads = []
for (let i = 0, I = field.bits / 8; i < I; i++) {
reads.unshift(`${cast.to}($buffer[$start++])`)
const shift = BigInt(shifts.shift())
if (shift != 0n) {
reads[0] += ` * ${hex(1n << shift)}${cast.suffix}`
const { shift, upper, mask } = combined.shift()
const bits = `${cast.to}($buffer[$start++])`
const masked = upper != 0
? `(${bits} & ${hex(mask)}${cast.suffix})`
: bits
if (shift == 0n) {
reads.push(`${masked}`)
} else {
reads.push(`${masked} * ${hex(1n << shift)}${cast.suffix}`)
}
}
$step += 2
Expand Down
49 changes: 47 additions & 2 deletions parse.inc.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,11 @@ function generate (packet, { require = null }) {
? `${path} = $lookup[${field.lookup.index}][$_]`
: `${path} = $lookup[${field.lookup.index}].forward[$_]`
: `${path} = $_`
// TODO Make these functions.
const cast = field.bits > 32
? { suffix: 'n', to: 'BigInt', fixup: '' }
: { suffix: '', to: '', fixup: ' >>> 0' }
// TODO Move this into the language, maybe even set the endianness.
const spread = function () {
if (field.spread == null) {
const spread = []
Expand All @@ -133,9 +135,52 @@ function generate (packet, { require = null }) {
}
return field.spread
} ()
const unrolled = ! spread.every(number => number == spread[0])
const upper = function () {
if (field.upper != null) {
return field.upper
}
return spread.map(() => 0)
} ()
const unrolled = ! (
spread.every(number => number == spread[0]) &&
upper.every(number => number == upper[0])
)
if (unrolled) {
throw new Error
const combined = spread.map((bits, index) => {
return {
mask: 0xff >>> 8 - bits,
shift: spread.slice(index + 1).reduce((sum, number) => sum + number, 0),
upper: upper[index]
}
})
const initialize = $(`
case ${$step++}:
$_ = 0${cast.suffix}
`)
const steps = join(combined.map(({ mask, shift, upper }) => {
const bits = upper != 0
? `${cast.to}($buffer[$start++] & ${mask}) << ${shift}`
: `${cast.to}($buffer[$start++]) << ${shift}`
return $(`
case ${$step++}:
if ($start == $end) {
$step = ${$step - 1}
`, inliner.exit(), `
return { start: $start, object: null, parse: $parse }
}
$_ += ${bits}
`)
}))
return $(`
`, initialize, `
`, steps, `
`, assign, `
`)
} else {
const multiplier = spread[0]
return $(`
Expand Down
20 changes: 16 additions & 4 deletions serialize.all.js
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,12 @@ function generate (packet, { require = null, bff, chk }) {
}
return field.spread
} ()
const upper = function () {
if (field.upper == null) {
return spread.map(() => 0)
}
return field.upper
} ()
function word (value, field) {
const writes = []
// TODO Ugly, expand and remove parens when not cast.
Expand All @@ -429,18 +435,24 @@ function generate (packet, { require = null, bff, chk }) {
const combined = spread.map((number, index) => {
return {
shift: spread.slice(index + 1).reduce((sum, number) => sum + number, 0),
mask: 0xff >>> 8 - number
mask: 0xff >>> 8 - number,
upper: upper[index]
}
})
if (field.endianness == 'little') {
combined.reverse()
}
for (let i = 0, I = field.bits / 8; i < I; i++) {
const { shift, mask } = combined.shift()
for (let i = 0, I = field.bits / 8; i< I; i++) {
const { shift, mask, upper } = combined.shift()
const shifted = shift != 0
? `${value} ${cast.shift} ${shift}${cast.to}`
: value
writes.push(`$buffer[$start++] = ${cast.from}(${shifted} & ${hex(mask)}${cast.to})`)
const lower = `${cast.from}(${shifted} & ${hex(mask)}${cast.to})`
if (upper == 0) {
writes.push(`$buffer[$start++] = ${lower}`)
} else {
writes.push(`$buffer[$start++] = ${lower} | ${upper}`)
}
}
return writes.join('\n')
}
Expand Down
52 changes: 49 additions & 3 deletions serialize.inc.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,58 @@ function generate (packet, { require = null }) {
}
return field.spread
} ()
const unrolled = ! spread.every(number => number == spread[0])
const upper = function () {
if (field.upper != null) {
return field.upper
}
return spread.map(() => 0)
} ()
const unrolled = ! (
spread.every(number => number == spread[0]) &&
upper.every(number => number == upper[0])
)
if (unrolled) {
throw new Error
const combined = spread.map((bits, index) => {
return {
mask: 0xff >>> 8 - bits,
shift: spread.slice(index + 1).reduce((sum, number) => sum + number, 0),
upper: upper[index]
}
})
const initialize = $(`
case ${$step++}:
`, assign, `
`)
const steps = join(combined.map(({ mask, shift, upper }) => {
const shifted = `${cast.from}($_ ${cast.shift} ${shift} & ${hex(mask)}${cast.suffix})`
const bits = upper != 0
? `${shifted} | ${hex(upper)}`
: shifted
return $(`
case ${$step++}:
if ($start == $end) {
$step = ${$step - 1}
`, inliner.exit(), `
return { start: $start, serialize: $serialize }
}
$buffer[$start++] = ${bits}
`)
}))
return $(`
`, initialize, `
`, steps, `
`)
} else {
const multiplier = spread[0]
const mask = 0xff >>> 8 - spread[0]
const shifted = `${cast.from}($_ ${cast.shift} $bite * ${multiplier}${cast.suffix} & ${hex(mask)}${cast.suffix})`
const masked = upper[0] != 0
? `(${shifted}) | ${hex(upper[0])}`
: shifted
return $(`
case ${$step++}:
Expand All @@ -97,7 +143,7 @@ function generate (packet, { require = null }) {
`, inliner.exit(), `
return { start: $start, serialize: $serialize }
}
$buffer[$start++] = ${cast.from}($_ ${cast.shift} $bite * ${multiplier}${cast.suffix} & ${hex(mask)}${cast.suffix})
$buffer[$start++] = ${masked}
$bite${direction}
}
Expand Down
18 changes: 17 additions & 1 deletion test/cycle/integer.t.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,23 @@ require('proof')(0, prove)
function prove (okay) {
const cycle = require('./cycle')
cycle(okay, {
name: 'integer/spread/short',
name: 'integer/spread/set/short',
define: {
object: {
nudge: 8,
value: [ 16, 0x80, 7, 0x0, 7 ],
sentry: 8
}
},
objects: [{
nudge: 0xaa,
value: 0x81,
sentry: 0xaa
}],
stopAt: 'parse.bff'
})
cycle(okay, {
name: 'integer/spread/unset/short',
define: {
object: {
nudge: 8,
Expand Down
3 changes: 3 additions & 0 deletions test/generated/integer/spread/set/short.lookup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function ({ $lookup }) {
$lookup.push.apply($lookup, [])
}
21 changes: 21 additions & 0 deletions test/generated/integer/spread/set/short.parser.all.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
module.exports = function ({ parsers, $lookup }) {
parsers.all.object = function () {
return function ($buffer, $start) {
let object = {
nudge: 0,
value: 0,
sentry: 0
}

object.nudge = ($buffer[$start++])

object.value =
(($buffer[$start++]) & 0x7f) * 0x80 +
($buffer[$start++])

object.sentry = ($buffer[$start++])

return object
}
} ()
}
27 changes: 27 additions & 0 deletions test/generated/integer/spread/set/short.parser.bff.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module.exports = function ({ parsers, $lookup }) {
parsers.bff.object = function () {
return function () {
return function ($buffer, $start, $end) {
let object = {
nudge: 0,
value: 0,
sentry: 0
}

if ($end - $start < 4) {
return parsers.inc.object(object, 1)($buffer, $start, $end)
}

object.nudge = ($buffer[$start++])

object.value =
(($buffer[$start++]) & 0x7f) * 0x80 +
($buffer[$start++])

object.sentry = ($buffer[$start++])

return { start: $start, object: object, parse: null }
}
} ()
}
}
35 changes: 35 additions & 0 deletions test/generated/integer/spread/set/short.parser.chk.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module.exports = function ({ parsers, $lookup }) {
parsers.chk.object = function () {
return function () {
return function ($buffer, $start, $end) {
let object = {
nudge: 0,
value: 0,
sentry: 0
}

if ($end - $start < 1) {
return parsers.inc.object(object, 1)($buffer, $start, $end)
}

object.nudge = ($buffer[$start++])

if ($end - $start < 2) {
return parsers.inc.object(object, 3)($buffer, $start, $end)
}

object.value =
($buffer[$start++]) * 0x80 +
($buffer[$start++])

if ($end - $start < 1) {
return parsers.inc.object(object, 5)($buffer, $start, $end)
}

object.sentry = ($buffer[$start++])

return { start: $start, object: object, parse: null }
}
} ()
}
}
Loading

0 comments on commit 65d4cc5

Please sign in to comment.