/**
* @file
* This file is about yaml encode.
*/
package yaml4cj.yaml
/**
* The Function is encode
*
* @param input of JsonValue
*
* @return Type of Array<UInt8>
* @since 0.30.4
*/
public func encode(input: JsonValue): Array<UInt8> {
initResolve()
let e = Encoder()
try {
e.encode("", input)
e.finish()
e.out.toArray()
} catch (err: Exception) {
throw err
} finally {
e.destroy()
}
}
var disableLineWrapping = false
class Encoder {
var emitter: EmitterT = EmitterT()
var event: EventT = EventT()
let out: ArrayList<UInt8> = ArrayList<UInt8>()
var flow: Bool = false
var doneInit: Bool = false
init() {
emitterInitialize()
emitterSetOutputString()
}
func emitterInitialize() {
this.emitter = EmitterT()
this.emitter.buffer = ArrayList<UInt8>(outputBufferSize, {_ => 0})
this.emitter.rawBuffer = ArrayList<UInt8>(outputRawBufferSize)
this.emitter.states = ArrayList<EmitterStateT>(initialStackSize)
this.emitter.events = ArrayList<EventT>(initialQueueSize)
if (disableLineWrapping) {
this.emitter.bestWidth = -1
}
}
func emitterSetOutputString() {
if (let Some(_) <- emitter.writeHandler) {
throw Exception("must set the output target only once")
}
emitter.writeHandler = stringWriteHandler
emitter.outputBuffer = this.out
}
func initiator() {
if (this.doneInit) {
return
}
this.event = streamStartEventinitialize(EncodingT_UTF8_ENCODING)
emit()
this.doneInit = true
}
func finish() {
this.emitter.openEnded = false
this.event = streamEndEventinitialize()
emit()
}
func destroy() {
this.emitter = emitterDelete()
}
func emit() {
must(emitterEmit(this.emitter, this.event, this))
}
func must(ok: Bool) {
if (!ok) {
let msg = if (this.emitter.problem == "") {
"unknown problem generating YAML content"
} else {
this.emitter.problem
}
failf(msg)
}
}
func encode(tag: String, input: JsonValue) {
initiator()
this.event = documentStartEventinitialize(true)
emit()
encodeValue(tag, input)
this.event = documentEndEventinitialize(true)
emit()
}
func encodeValue(tag: String, input: JsonValue): Unit {
if (input is JsonNull) {
nilv()
} else if (input is JsonObject) {
mapv(tag, (input as JsonObject).getOrThrow())
} else if (input is JsonArray) {
slicev(tag, (input as JsonArray).getOrThrow())
} else if (input is JsonString) {
stringv(tag, (input as JsonString).getOrThrow())
} else if (input is JsonInt) {
intv(tag, (input as JsonInt).getOrThrow())
} else if (input is JsonFloat) {
floatv(tag, (input as JsonFloat).getOrThrow())
} else if (input is JsonBool) {
boolv(tag, (input as JsonBool).getOrThrow())
} else {
throw Exception("cannot marshal type: ${input}")
}
}
func nilv() {
emitScalar("null", "", "", ScalarStyleT_PLAIN_SCALAR_STYLE)
}
func emitScalar(value: String, anchor: String, tag: String, style: ScalarStyleT) {
let implicit = tag == ""
let (eve, ok) = scalarEventInitialize(
anchor.toArray(),
tag.toArray(),
value.toArray(),
implicit,
implicit,
style
)
this.event = eve
must(ok)
emit()
}
func mapv(tag: String, input: JsonObject) {
mappingv(tag, {
=>
let kv = input.getFields()
for ((k, v) in kv) {
encodeValue("", JsonString(k))
encodeValue("", v)
}
})
}
func mappingv(tag: String, f: () -> Unit) {
let implicit = tag == ""
var style = MappingStyleT_BLOCK_MAPPING_STYLE
if (this.flow) {
this.flow = false
style = MappingStyleT_FLOW_MAPPING_STYLE
}
this.event = mappingStartEventInitialize([], tag.toArray(), implicit, style)
emit()
f()
this.event = mappingEndEventInitialize()
emit()
}
func slicev(tag: String, input: JsonArray) {
let implicit = tag == ""
var style = SequenceStyleT_BLOCK_SEQUENCE_STYLE
if (this.flow) {
this.flow = false
style = SequenceStyleT_FLOW_SEQUENCE_STYLE
}
let (eve0, ok0) = sequenceStartEventInitialize([], tag.toArray(), implicit, style)
this.event = eve0
must(ok0)
emit()
for (i in 0..input.size()) {
encodeValue("", input[i])
}
let (eve1, ok1) = sequenceEndEventInitialize()
this.event = eve1
must(ok1)
emit()
}
func stringv(ntag: String, input: JsonString) {
var s = input.getValue()
var canUsePlain = true
var tag = ntag
if (!utf8ValidString(s)) {
/* if (tag == YAML_BINARY_TAG) {
failf("explicitly tagged !!binary data must be base64-encoded")
}
if (tag != "") {
failf("cannot marshal invalid UTF-8 data as ${shortTag(tag)}")
}
tag = YAML_BINARY_TAG
s = encodeBase64(s)*/
} else if (tag == "") {
let (rtag, _) = resolve("", s)
canUsePlain = rtag == YAML_STR_TAG && !isBase60Float(s)
}
let style = if (s.contains("\n")) {
ScalarStyleT_LITERAL_SCALAR_STYLE
} else if (canUsePlain) {
ScalarStyleT_PLAIN_SCALAR_STYLE
} else {
ScalarStyleT_DOUBLE_QUOTED_SCALAR_STYLE
}
emitScalar(s, "", tag, style)
}
func intv(tag: String, input: JsonInt) {
emitScalar("${input.getValue()}", "", tag, ScalarStyleT_PLAIN_SCALAR_STYLE)
}
func floatv(tag: String, input: JsonFloat) {
let v = input.getValue()
let s = if (Float64.Inf == v) {
".inf"
} else if (-Float64.Inf == v) {
"-.inf"
} else if (Float64.NaN == v) {
".nan"
} else {
"${v}"
}
emitScalar(s, "", tag, ScalarStyleT_PLAIN_SCALAR_STYLE)
}
func boolv(tag: String, input: JsonBool) {
let s = if (input.getValue()) {
"true"
} else {
"false"
}
emitScalar(s, "", tag, ScalarStyleT_PLAIN_SCALAR_STYLE)
}
}
func stringWriteHandler(emitter: EmitterT, buffer: ArrayList<UInt8>) {
emitter.outputBuffer.add(all: buffer)
}
func emitterDelete(): EmitterT {
EmitterT()
}
func streamStartEventinitialize(encoding: EncodingT): EventT {
let e = EventT()
e.typ = EventTypeT_STREAM_START_EVENT
e.encoding = encoding
e
}
func streamEndEventinitialize(): EventT {
let e = EventT()
e.typ = EventTypeT_STREAM_END_EVENT
e
}
func documentStartEventinitialize(implicit: Bool): EventT {
let e = EventT()
e.typ = EventTypeT_DOCUMENT_START_EVENT
e.versionDirective = None
e.tagDirectives.clear()
e.implicit = implicit
e
}
func documentEndEventinitialize(implicit: Bool): EventT {
let e = EventT()
e.typ = EventTypeT_DOCUMENT_END_EVENT
e.implicit = implicit
e
}
func emitterEmit(emitter: EmitterT, event: EventT, encoder: Encoder): Bool {
emitter.events.add(event)
while (!emitterNeedMoreEvents(emitter)) {
let e = emitter.events[emitter.eventsHead]
if (!emitterAnalyzeEvent(emitter, e)) {
return false
}
let (eve, ok) = emitterStateMachine(emitter, e)
encoder.event = eve
if (!ok) {
return false
}
encoder.event = eventDelete()
emitter.eventsHead++
}
true
}
func eventDelete(): EventT {
EventT()
}
let base60float = Regex("^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\\.[0-9_]*)?$")
func isBase60Float(s: String): Bool {
if (s == "") {
return false
}
let c = s[0]
if (!(c == b'+' || c == b'-' || c >= b'0' && c <= b'9') || match (s.indexOf(b':')) {
case None => true
case _ => false
}) {
return false
}
return base60float.matches(s)
}