// 规范导出。
//
// 为什么不用 `Json::stringify`:`Json::Object` 底层是 Map,迭代顺序未定义,
// 落盘/指纹用的文本绝不能依赖它(计划 §3.2)。
// 这里用完全显式的顺序:specials 按 id 升序,tokens 按 id 升序,行尾 `\n`。

///|
fn push_str(out : Array[Char], s : String) -> Unit {
  for c in s.to_array() {
    out.push(c)
  }
}

///|
fn push_int(out : Array[Char], n : Int) -> Unit {
  push_str(out, n.to_string())
}

///|
/// 导出为自有规范文本格式(`tally-vocab v1`)。
///
/// 不变量 V10:`load(root_export(v))` 与 `v` 内容等价,
/// 且同样的 `v` 导出结果逐字节稳定。
pub fn Vocab::to_text(self : Vocab) -> String {
  let out : Array[Char] = []
  push_str(out, format_header)
  push_str(out, "\n")
  push_str(out, "# unicode ")
  push_str(out, @bpe.unicode_version())
  push_str(out, "\n")
  push_str(out, "# tokens ")
  push_int(out, self.token_count())
  push_str(out, "\n")
  push_str(out, "[specials]\n")
  for e in self.specials {
    let (lit, id) = e
    push_int(out, id)
    out.push(' ')
    push_str(out, b64_encode(@utf8.encode(lit)))
    out.push('\n')
  }
  push_str(out, "[tokens]\n")
  for id = 0; id < self.tokens.length(); id = id + 1 {
    let bytes = self.tokens[id]
    let rank = match self.rank_of(bytes) {
      Some(r) => r
      None => id
    }
    push_int(out, id)
    out.push(' ')
    push_int(out, rank)
    out.push(' ')
    push_str(out, b64_encode(bytes))
    out.push('\n')
  }
  String::from_array(out)
}