Skip to content

Commit

Permalink
Refactor decode and fix bools (#10)
Browse files Browse the repository at this point in the history
* always define all props and do it in one go

* fix required bools
  • Loading branch information
mafintosh authored Dec 19, 2024
1 parent 62c5073 commit 84574ab
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 19 deletions.
7 changes: 7 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ class Struct extends ResolvedType {

for (let i = 0; i < description.fields.length; i++) {
const fieldDescription = description.fields[i]

// bools can only be set in the flag, so auto downgrade the from required
// TODO: if we add semantic meaning to required, ie "user MUST set this", we should
// add an additional state for this
if (fieldDescription.required && fieldDescription.type === 'bool') {
fieldDescription.required = false
}
const flag = !fieldDescription.required ? 2 ** this.optionals.length : 0
const field = new StructField(hyperschema, this, i, flag, fieldDescription)

Expand Down
53 changes: 34 additions & 19 deletions lib/codegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,42 +174,57 @@ module.exports = function generateSchema (hyperschema) {
function generateDecode (struct) {
let str = ''

str += ' const res = {}\n'
const pre = new Map()
for (let i = 0; i < struct.fields.length; i++) {
const field = struct.fields[i]
str += ` ${vPrefix(field.version)}${gen('res', field.name)} = ${field.type.default}\n`
if (!field.required || field.type.bool || i === struct.flagsPosition) break
pre.set(field, 'r' + i)
str += ` const r${i} = ${getFieldEncoder(field)}.decode(state)\n`
}

if (pre.size === struct.flagsPosition) {
str += ' const flags = state.start < state.end ? c.uint.decode(state) : 0\n\n'
} else if (pre.size) {
str += '\n'
}
str += '\n'

str += ' return {\n'

for (let i = 0; i < struct.fields.length; i++) {
const field = struct.fields[i]
let decodeStr = ''
if (field.type.bool) {
decodeStr = `${gen('res', field.name)} = true`
} else {
decodeStr = `${gen('res', field.name)} = ${getFieldEncoder(field)}.decode(state)`
}
const end = i < struct.fields.length - 1 ? ',\n' : '\n'
const flag = vInlinePrefix(field.version, `(flags & ${field.flag}) !== 0`)
const prop = gen.property(field.name)

if (i === struct.flagsPosition) {
if (i !== 0) str += '\n'
str += ' const flags = state.start < state.end ? c.uint.decode(state) : 0\n'
if (pre.has(field)) {
str += ` ${prop}: ${pre.get(field)}${end}`
continue
}
if (field.type.bool) {
str += ` ${prop}: ${flag}${end}`
continue
}

if (field.required) {
str += ` ${decodeStr}\n`
} else {
str += ` ${vPrefix(field.version, `(flags & ${field.flag}) !== 0`)} ${decodeStr}\n`
str += ` ${prop}: ${getFieldEncoder(field)}.decode(state)${end}`
continue
}

str += ` ${prop}: ${flag} ? ${getFieldEncoder(field)}.decode(state) : ${field.type.default}${end}`
}

if (struct.fields.length !== 0) str += '\n'
str += ' return res'
str += ' }'

return str
}

function vPrefix (v, condition) {
if (v === 1) return condition ? `if (${condition})` : ''
return condition ? `if ((version >= ${v}) && ${condition})` : `if (version >= ${v}) `
return condition ? `if (version >= ${v} && ${condition})` : `if (version >= ${v}) `
}

function vInlinePrefix (v, condition) {
if (v === 1) return condition || ''
return condition ? `(version >= ${v} && ${condition})` : `version >= ${v}`
}

function getFieldEncoder (field) {
Expand Down

0 comments on commit 84574ab

Please sign in to comment.