网站地图 | Tags | 热门标准 | 最新标准 | 订阅
您当前的位置:首页 > GB/T 44596-2024 中文域名编码技术要求 > 下载地址1

GB/T 44596-2024 中文域名编码技术要求

  • 名  称:GB/T 44596-2024 中文域名编码技术要求 - 下载地址1
  • 下载地址:[下载地址1]
  • 提 取 码
  • 浏览次数:3
下载帮助: 发表评论 加入收藏夹 错误报告目录
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表
新闻评论(共有 0 条评论)

资料介绍

  ICS 33. 040.40 CCS M 32

  中 华 人 民 共 和 国 国 家 标 准

  GB/T 44596—2024

  中文域名编码技术要求

  Technicalspecification forcoding ofChinesedomain names

  2024-09-29发布 2025-01-01实施

  国家市场监督管理总局国家标准化管理委员会

  

  发

  

  布

  GB/T 44596—2024

  目 次

  前言 Ⅲ

  1 范围 1

  2 规范性引用文件 1

  3 术语 、定义和缩略语 1

  3. 1 术语和定义 1

  3. 2 缩略语 3

  4 字符串预处理和中文域名字段预处理 3

  4. 1 字符串预处理框架 3

  4. 2 字符预备处理概要 4

  4. 3 映射 5

  4. 4 归一化 5

  4. 5 禁止输出 6

  4. 6 双向字符 6

  4. 7 字符串预处理框架中的未分配码位 6

  4. 8 中文域名字段预处理 7

  5 Punycode编码 7

  5. 1 Punycode特点 7

  5. 2 Bootstring架构 8

  5. 3 Bootstring参数 9

  5. 4 Punycode参数值 9

  5. 5 Bootstring算法 10

  附录 A (规范性) UNICODE保留字符表 13

  附录 B (规范性) 映射表 14

  附录 C (规范性) 禁止输出表 15

  附录 D (规范性) 双向字符表 17

  附录 E (资料性) Punycode编码解码例子 18

  附录 F (资料性) Punycode算法编码解码 C语言实现例子 20

  Ⅰ

  GB/T 44596—2024

  前 言

  本文件按照 GB/T 1. 1—2020《标准化工作导则 第 1部分 :标准化文件的结构和起草规则》给出的规定起草 。

  本文件为中文域名技术系列标准之一 ,结构如下 :

  — 中文域名总体技术要求 ;

  — 中文域名编码技术要求 ;

  — 中文域名解析技术要求 ;

  — 中文域名注册技术要求 ;

  — 中文域名字表技术要求 。

  请注意本文件的某些内容可能涉及专利 。本文件的发布机构不承担识别专利的责任 。

  本文件由中华人民共和国工业和信息化部提出 。

  本文件由全国通信标准化技术委员会(SAC/TC485)归 口 。

  本文件起草单位 : 中国互联网络信息中心 、暨南大学 、中国科学院计算机网络信息中心 、广东盈世计算机科技有限公司 、中国信息通信研究院 、清华大学 、中国电信股份有限公司 、中国联合网络通信集团有限公司 、中国移动通信集团有限公司 、中国通信标准化协会 。

  本文件主要起草人 :姚健 康 、耿 光 刚 、翁 健 、李 洪 涛 、李 彦 彪 、段 海 新 、张 曼 、周 琳 琳 、董 科 军 、杨 学 、延志伟 、孔令飞 、沙晓爽 、傅瑜 、刘越 、龙春 、赵静 、郑晖 、王超 、孙乐 、吴秀诚 、张文伟 。

  Ⅲ

  GB/T 44596—2024

  中文域名编码技术要求

  1 范围

  本文件规定了在互联网体系上使用中文域名的编码规范 , 以及使用中文域名字段的预处理要求 , 同时规定了利用国际区域名码进行中文域名编码的技术要求 。

  本文件适用于各级域名注册管理机构 、域名注册服务提供商以及软件厂商开发支持中文域名的应用或者服务等 。

  2 规范性引用文件

  下列文件中的内容通过文中的规范性引用而构成本文件必不可少的条款 。其中 , 注 日期的引用文件 ,仅该日期对应的版本适用于本文件 ;不注日期的引用文件 ,其最新版本(包括所有的修改单) 适用于本文件 。

  IETF RFC 3454 国际化字符串预处理(Preparation ofinternationalized strings)

  IETF RFC 3491 国际化域名预处理(Nameprep: a stringprep profile for internationalized do-

  main names)

  UNICODE

  UAX15

  通 用 字 符 编 码 标 准 附 录 15(The unicode consortium unicode standard

  Annex # 15)

  UNICODE

  UTR21

  通用字符编码技术规范 21(The unicode consortium unicode technical Re-

  port # 21)

  3 术语、定义和缩略语

  3. 1 术语和定义

  下列术语和定义适用于本文件 。

  3. 1. 1

  通用字符编码 unicodeconsortium unicodeencoding

  根据其位置或码位来识别字符 ,给每个字符提供的一个唯一的数字 。

  注 : 比如 , U+4E96指 的 是 在 UNICODE 字 符 集 中 位 于 4E96处 的 字 符 。本 文 件 的 UNICODE 字 符 编 码 采 用 了ISO/IEC 10646-1,UNICODE字符编码的集合称为 UNICODE字符集 。

  3. 1.2

  分隔符 delimiter

  在 punycode里用来分隔的符号“-”。

  3. 1.3

  域名字段 domain namelabel

  域名中由分隔符隔开的几个部分 。

  注: 对于一个完整的域名“www. cnnic.cn.”,其中“www”“cnnic”“cn”分别是三个域名字段。

  1

  GB/T 44596—2024

  3. 1.4

  中文域名字段 Chinesedomain namelabel

  含有中文字符的域名字段 。

  3. 1.5

  中文域名 Chinesedomain name

  含有中文域名字段的域名 。

  3. 1.6

  码位 codepoints

  字符的数字表现形式 。

  注 : 在 UNICODE里 ,UNICODE码位用“U+ ”开始的 4~ 6位 16进制数表示 。码位序列可用没有前缀的用空格分隔的两位一组的十六进制数表示 。

  3. 1.7

  溢出 overflow

  计算时超出了最大整数值 。

  3. 1. 8

  国际化域名码 punycode

  一种编码转换规则 。

  注 : 运用这种规则可实现 UNICODE字符编码字符串和 LDH 字符串的相互转换 。

  3. 1.9

  LDH 编码前缀 lettersdigitshyphen prefix

  注 1: LDH 即 Letter、Digit和 Hyphen三个英文单词的首字母简写 。 由两个 LDH 字符后跟着两个连 字 符(其 中 字母不区分大小写)进行表示的前缀 。

  注 2: 用于中文域名的 LDH 编码前缀是“xn--”。

  3. 1. 10

  存储字符串 stored string

  用在协议标识符和名称实体中的字符串 。

  3. 1. 11

  查询字符串 querystring

  用于和已保存的标识符相匹配或者相比较的字符串 。

  示例 : 域名查询中的域名部分 。

  3. 1. 12

  先分解后合成的归一化形式 normalization form with compatibility composition

  UNICODE归一化的一种形式 。

  注 : 先对字符进行兼容性分解 ,再对其进行规范合成 。

  3. 1. 13

  基础字符 basiccodepoints

  小范围字符集里的字符 。

  注 : 本文件一般指 ASCII字符集中的字符 。

  3. 1. 14

  非基础字符 non-basiccodepoints

  大范围字符集里的字符 。

  注 : 本文件一般指 UNICODE字符集里的字符(ASCII字符除外 ,ASCⅡ码等同于 GB/T 1988规定的字符编码) 。

  2

  GB/T 44596—2024

  3. 1. 15

  基础字串 basicstring

  所有的字符都是基础字符的字串 。

  3. 1. 16

  扩展字串 extended string

  包含非基础字符的字串 。

  3. 1. 17

  字符串预处理 stringprep

  对字符串进行处理的过程 。

  3. 1. 18

  名字预处理 nameprep

  对名字进行处理的过程 。

  注 : 其目的是使其能用于域名字段 ,或者检查其是否能用于域名字段 。

  [来源 :IETF RFC 3491,第 1 章]

  3.2 缩略语

  下列缩略语适用于本文件 。

  CDN: 中文域名(Chinese Domain Name)

  CDNA: 中文域名应用(Chinese Domain Names in Applications)

  LDH :字母 、数字 、连接符(Letters Digits Hyphen)

  NFKC:形式的规一化(Normalization Form with Compatibility Composition)

  UNICODE:通用字符编码(Unicode Consortium Unicode Encoding)

  4 字符串预处理和中文域名字段预处理

  4. 1 字符串预处理框架

  字符串预处理框架确立了一个包含处理 UNICODE字符规则的框架模型 。其他协议可根据这些规则定义自己的框架 ,这些框架可使用户在应用程序中输入国际化字符串 ,并且正确地得到处理 。本文件不规定如何把非 UNICODE码位转化成 UNICODE码位 。为了更好地做好字符映射 ,字符串预处理框架可有效地排除那些不宜作为中文域名字段的字符 。字符串预处理框架输入的是字串 ,输出是字串或错误信息 。字符串预处理框架不能解决用户期望的所有字符变体问题 。字符串预处理框架应使用附录 A、附录 B、附录 C 和附录 D 中的字表来处理字 串 。 字 符 串 预 处 理(string grep) 应 符 合 IETF RFC 3454的规定 。

  字符串预处理框架应遵循下列特性 :

  a) 框架所应具有的能力 ;

  b) 字表 ,本文件采用 UNICODE3. 2 字表 ;

  c) 映射表 ;

  d) 所用的归一化 ;

  e) 禁止字符输出表 。

  字符串预处理框架应明确所用的字表 , 任何字表都是不完善的 , 总有一些字会在将来被加进字表中 。本文 件 的 4. 7 专 门 规 定 了 如 何 处 理 字 表 中 新 增 加 的 字 符 问 题 。 本 文 件 在 附 录 A 中 列 出了 0~ 10FFFF 中未被分配的码位 。 附录 A 中的码位应被本文件使用 。如果附录 A 和 UNICODE3. 2 有任何冲突 ,本文件优先附录 A 中的码位 。

  3

  GB/T 44596—2024

  4.2 字符预备处理概要

  字符预备处理的步骤如下 。

  a) 映射—对输入的任何字符检查是 否 拥 有 映 射 , 如 果 有 , 就 使 用 映 射 字 符 代 替 。 在 4. 3 中 详 细规定 。

  b) 归一化使用 UNICODE归一化来尽可能地归一化第一步处理的结果 。在 4. 4 中详细规定 。

  c) 字符 “禁止输出 ”步骤检查输出中是否有任何禁止输出的字符 , 如果有 ,则返回错误信息 。 在

  4. 5 中详细规定 。

  d) 检查双向字符步骤检查是否有从右向左书写的文字 , 如果有 ,则需要确认整个字符满足双向字符的要求 。如果不满足双向字符的要求 ,则返回错误信息 。对于中文域名 ,本文件在 4. 6 中规定不准许在中文域名里使用双向字符 , 因此如果中文域名字段里出现双向字符 ,则返回错误信息 。

  上述步骤应按本文 件 的 规 定 执 行 , 步 骤 流 程 图 见 图 1。 映 射 中 和 归 一 化 中 所 说 的 映 射 可 是 一 对零 ,一对一 ,一对多 ,多对多或多对一映射 ,所以输出的字串有可能比原输入字串长或短 。

  图 1 字符串预处理步骤

  4

  GB/T 44596—2024

  4.3 映射

  输入串中的每个字符应接受一个映射表的检查 ,这个映射表应来自本文件 ,也可在具体实现中改动或者添加 。此映射表在本文件的附录 B 中给出 。

  本文件 的 任 何 实 现 者 应 使 用 附 录 B 中 给 出 的 映 射 列 表 。 如 果 标 准 中 的 任 何 规 定 与 附 录 B 有 冲突 ,应遵循附录 B。

  对于任何单个字符 , 映射表中可将其映射为空 ,另外的一个单个字符或者其他的一串字符 。

  经过映射处理的字符就不应再被映射处理 。下面是两种用到的映射 。

  a) 经常使用的映射到空字表 ,有些字符的存在与否对区分两个字串没有任何帮助 ,这些字符将直接从输入中删除(也就是映射到空) 。具体见附录 B。

  b) 小写转换 。

  如果字符串预处理框架准备使用不区分大小写的比较方式进行字符映射 ,则它应使用附录 B 中的B. 2 或者 B. 3 中任意一个映射表 。B. 2 是为使用了 NFKC形式的 UNICODE归一化的框架提供的 , 而附录 B 中的 B. 3是为没有使用 UNICODE归一化的框架提供的 。这些表都是将大写字母映射到小写字母 。转换的原因是目前大多数 Internet程序和协议中都使用小写字母 。

  如果一个框架建立自己的小写转换映射表 ,则应依据 UNICODE UTR21,并且应映射大写字母到小写字母 。从 UNICODE数据库得到的“CaseFolding. txt”文件也应用于建立这个映射表 。

  如果一个框架使用 了 NFKC 形 式 的 UNICODE 归 一 化 , 则 应 注 意 一 点:有 些 字 符 在 UNICODE UTR21中不存在映射 ,但仍需进行处理 。这些字符包括一些希腊字母和许多包括拉丁字符的符号 。需要加入映射表的字符列可由下列算法决定 :

  b= NormalizeWithKC[Fold(a)] ;

  c(o)r与 b(mal)i不相(zeW)i同(th)K,则增加(C[Fo)ld一( a到 c的映射 。

  因为 NormalizeWithKC(Fold(c))总是等于 c,所以这个表是稳定的 。

  附录 B 中 B. 3是源自和 UNICODE3. 2相关联的 CaseFolding-3. txt文件 。 而附录 B 中的 B. 2 是在附录 B 中的 B. 3 的基础上增加由以上算法所确定的增加字符额外映射 。

  字符串预处理需要更新框架时 ,如果改变任何目前已分配码位的映射 ,需要注意这个改变的影响 。对已分配码位增加一个映射或者改变一个映射都有可能导致一个已升级的系统和另一个没有升级的系统之间的不一致性 。

  4.4 归一化

  映射之后的输出可选择使用 UNICODE UAX15中规定的一种 UNICODE归一化形式进行归一化处理 ,归一化可使等效的字符有相同的二进制表示形式 。字符串预处理框架可选择使用两种归一化方式中的一种 :

  a) 不进行归一化 ;

  b) 使用 KC形式的 UNICODE归一化 。

  字符串预处理框架可选择不进行归一化 。然而这样的框架可能会使一些用户发现结果和输入不一致 。 比如一些输入机制可输入一些字形上容易混淆的兼容字 ,结果却是两个截然不同的码位 。

  虽然 UNICODE归一化需要相当大的字表和有点复杂的字符排列逻辑 , 除非在特殊情况下 , 字符串预处理框架都应使用归一化 。本文件并没有给出 UNICODE归一化所需要的表 ,但是这些表应是源自 UNICODE UAX15的定义的 UNICODE库 。

  还存在第三种归一化形式 ,就是 C形式的 UNICODE归一化 。如果一个框架要使用 UNICODE归一化则其应使用 NFKC形式的 UNICODE归一化 。NFKC形式的归一化将很多兼容字符映射成它们

  5

  GB/T 44596—2024

  的等效字符 。有一些用户界面允许用户输入一些兼容性字符而不是原本的等效字符 ,所以使用 NFKC形式的归一化来代替 C形式的归一化会使更多的字符得到正确的映射 。

  凡是指定了 UNICODE归一化的字符串预处理框架应使用在 UNICODE UAX15 中规定的 ,并且与本文件中指定的 UNICODE字符集相配套的归一化形式 。

  4.5 禁止输出

  在文本输出之前 ,一定要进行 “禁止字符 ”的检查 。字符串预处理框架应使用附录 C 中定义的全部或者部分 “禁止输出 ”表 。

  字符串预处理处理过程不会既返回错误信息又返回一个字符串 ,如果在检查 “禁止输出 ”的过程中发生任何错误 ,则只返回错误信息 。

  “禁止输出 ”的字符主要有以下几类 :

  a) 空格字符 ;

  b) 控制字符 ;

  c) 专用字符 ;

  d) 非字符码位 ;

  e) 代理码位 ;

  f) 与无格式字符不相称的 ;

  g) 与标准表示不相称的 ;

  h) 改变显示属性或者被 UNICODE摒弃的 ;

  i) 标签字符 。

  “禁止字符 ”表见附录 C,任何框架实现的时候可增加别的 “禁止输出 ”字符 。

  4.6 双向字符

  大多数字符是从左到右显示的 ,但是有些字符是从右到左显示的 ,称为 “双向字符 ”, 附录 D 列出了双向字符表 。本文件不准许双向字符在中文域名中使用 ,如果检测到双向字符 ,应给出错误信息 。

  4.7 字符串预处理框架中的未分配码位

  国际化字符串分为 “存储字符串 ”和 “查询字符串 ”两种类型 。“存储字符串 ”是指用在协议表示符和名称实体中的字符串 , 比如数字证书中的名称和 DNS域名部分 。“查询字符串 ”是指用于与已保存的标示符相匹配或者相比较的字符串 , 比如用户敲入的数字证书的名字和 DNS查询中的名称 。

  在字符串预处理框架中没有被分配字符的所有码位都被称为 “未分配码位 ”。使用这个框架的存储字符串不准许包含任何未分配码位 。而用于匹配字符串的查询字符串可包含未分配码位 。

  字符串预处理框架应给出未分配码位列表 ,并且这个列表应被这个框架的实施者使用 。

  本文件不准许两个包含未分配码位的字符串之间的比较 。 当两个字符串 X 和 Y进行比较 ,并且字符串 Y是经过允许包含未分配码位的方式处理得到的 ,如果比较结果是不匹配 ,则这个结果具有不确定性 ;此时很有可能的结果是 :两个串不匹配 ,但是当对字符串 Y使用一个更新的框架的时候结果却匹配了 。如果当两个字符串 X 和 Y都是经过允许包含未分配码位的方式处理就有可能发生更坏的结果 :即使比较结果是匹配 ,也具有不确定性 ,此时很有可能的结果是 :两个串匹配而使用一个更新的框架的时候结果却不匹配了(当一个码位在 X 和 Y 中都被不应被输出) 。

  本文件不准许存储字符串包含任何未分配码位 。

  代码位的种类如下 :

  在字符串预处理框架中定义的每个码位都可根据它在上述步骤中的操作方式进行分类 :

  a) AO,可在输出中出现的代码位 ;

  6

  GB/T 44596—2024

  b) MN , 因为映射或者归一化而不可能出现在输出中的代码位 ;

  c) D, 因为在 “禁止输出 ”步骤中不准许出现在输出中的代码位 ;

  d) U ,未分配的代码位 。

  新版本的字 符 串 预 处 理 框 架 中的 字 表 会 出 现 新 的 码 位 , 这 些 新 的 码 位 应 从 U 分 类 中 转 移 到MN ,AO 或者 D分类 。为了向后的兼容性 ,新版本的框架不应从别的任何分类中转移码位 , 即当前在MN ,AO 和 D分类中的码位一定不能转移到其他分类中 。

  最新版本字符串预处理框架中的存储字符串不应包含除了 AO 分类之外的码位 , 既不应包含任何MN , D, 以及 U分类中的码位 。

  应用程序在创建一个查询时 ,这个应用程序在输入时应把 U类代码位当作 AO 类代码位看待 。这些应用程序可有选择地使用一个提供严格字符检查的预处理程序 :把输入中出现的未分配代码位当作错误或者警告用户本软件中的当前版本的框架识别出未分配代码位 。

  4. 8 中文域名字段预处理

  中文域名字段预处理是字符串预处理的一种框架 ,实现中文域名字段预处理应首先完全实现字符串预处理 。 中文域名字段预处理是用来直接处理中文域名字段的 ,而不是整个域名 。CDNA对每个域名字段会调用中文域名字段预处理 。 中文域名字段预处理使用附录 A 中的字表 ; 映射时使用附录 B;归一化时使用 NFKC形式的归一化 ; “禁止输出 ”字符应使用附录 C;如果应用程序在实现 CDNA 时使用未分配码点列表 ,应使用附录 A 中的字表 。

  5 Punycode编码

  5. 1 Punycode特点

  [CDNA]规定了支持 CDN 的架构 。非 ASCII形式的域名字段可用 ACE前缀开始的 ACE形式表示 。后缀是 UNICODE字串的 punycode编码 。Punycode是一种为国际化多语种域名设计的一种简单高效 的 转 换 编 码 方 法 。 它 唯 一 且 可 逆 转 地 把 UNICODE 字 串 转 换 成 ASCII字 串 。 UNICODE 里 的ASCII字符还是按原来的字符显示 ,非 ASCII字符用 ASCII字符中 LDH 表示 。本文件规定了一种叫bootstring的通用算法来用基础码位来表示大字符集中的码位串 。Punycode是 bootstring算法的特殊形式 ,它使用了专门的参数使其符合国际化多语种域名应用的需要而设计 ,Punycode主要是用来把中文域名字段转换成 ASCII形式 。

  Bootstring被设计成有下列特点 。

  a) 完备性 :任意扩展的字串可被基础字串表示 ,对字串的限制可在应用的高层来做 。

  b) 唯一性 :至多只有一个基础字串表示扩展字串 。

  c) 可逆转性 :任何被表示成基础字串的扩展字串可从基础字串转换回来 。

  d) 编码高效性 :基础字串的长度和扩展字段的长度的比值很小 。

  e) 简单性 :编码和解码方法容易实现 。

  f) 可读性 :扩展字串中的基础码位仍旧被表示成原来的码位 。

  Bootstring把扩展字串表示成基础字串 。 随后的几小节规定了 bootstring里的 4 项技术 。基础码位分离是一次性把扩展字串中的基础码位一次性地复制出来 。插入未整理的编码是把非基础码位分成堆 ,这些堆的排列不是根据码位出现的顺序而是码位所代表的数字顺序 ,这样可导致更小的堆 。这些堆可被表示成通用可变长整数 ,然后可再用基础码位来表示 。 当连续的堆有相似的量级的时候 ,这些整数表示可用贝叶斯调解“Bias adaptation”来动态调解参数 ,增强有效性 。

  7

  GB/T 44596—2024

  5.2 Bootstring架构

  5.2. 1 基础码位分离

  所有出现在扩展字串里的基础码位按它们原来的出现顺序依次排在基础串的前面作为基础串的前半部分 。基础串的前半部分和后半部分用分隔符分开 。分隔符是一个特殊的符号 ,它从来不会出现在基础串的后半部分 。

  5.2.2 插入未排序码位

  基础串的后半部分把非负整数形成的堆的序列表示成通用可变长整数 , 这一点主要在 5. 2. 3 中规定 。在这里可把堆看成解码器 。解码器逐渐建立扩展串 。开始时 ,扩展串是基础串中前半部分的完全复制(不包括分隔符) 。解码器为每个堆建立一个非 ASCII码位插入到扩展串中 ,依次解码 ,最后完全解码 。这个过程的核心部分是状态机 。状态机用序号 i(index i) 和计数器 n(countern) 表示 。i表示在基础串中的 位 置 , 它 的 值 的 变 化 范 围 是 0(第 一 个 位 置) 到 当 前 的 串的 长 度 。 假 如 当 前 的 状 态 是 ,下一个状态就是(i小于当前串的长度) 或者 (i等于当前串的长度) 。每次状态变化引起 i增加 , 当 i等于当前串的长度时 ,i就会被置 0, 同时 n 加 1。状态总是单向增长 ,不会返回到原来的状态 。在每个状态 ,插入操作可进行也可不进行 。对于任何一个状态最多只能进行 一次插入操作 。每次插入操作都是把 n 的值放在当前扩展串的第 i个位置 。 堆是一系列活动的实时编码 :堆是在插入状态之 前 的 非 插 入 状 态 的 个 数 。对 每 个 堆 , 解 码 器 执 行 堆 状 态 变 化 , 然 后 执 行 插 入 操作 ,然后再下一个状态变化 ,在实现时 ,不必每次单独执行状态变化 ,可用除法和取余运算来直接算出下次插入状态 。如果插入的码位是基础码位会产生错误 。

  编码器主要任务是从扩展串中得到堆的序列 。编码器不停地扫描扩展串中的下个码位 ,算出解码器需要执行的状态变化次数 。 同时 ,要注意解码中的扩展串只会包括那些已经被插入的码位 。 6. 3 会给出确切的算法 。

  5.2.3 普通可变长整数

  在传统的整数表示方法中 ,基数(base)就是 0~ base减 1 个数字符号的个数 。 digit_ 0 代表权重最

  小的数 ,digit_ 1 代 表 权 重 下 个 最 小 的 数 , 依 次 排 列 。 整 数 所 表 示 的 值 是 所 有 digit_j 乘 以 w(j) 的 和

  [w(j)是 digit_j所处位置的权值] 。这种表示方法有两个缺点:第一 ,对每个数值有多种编码方法 ,对要

  求单一编码的操作很不方便 ;第二 ,如果多个整数连接在一起 ,不能根据自己的特点把它们分开 ,这些整

  数之间的界限就消失了 。通用可变长表示法解决了上述两个问题 。 每个数字符号值依然是 0 到 base

  digit_j

  减 1。整数可以通过极限 t(j)(0=

  积的总和 ,但每个权值是不同的 : w(0)= 1;

  值 ,把 t更新到下个位置的值 ,这种过程不断重复 。编码过程也和传统整数的编码过程类似 。如果 N<

  后令)和-);整(果(b)解(e)当程(-)极)常,程停止(前值 N)=,否则(0和)权,根据(重)(ba(开)-;t(取))来(出)获(下)得(个)新的(整数),

  (base-t) ,更新下个位置 t 的值 ; 以上过程不断重复 。对于任何特定的 t(j)的值集合 ,每个非负整数只

  t则输出一位数 字 , 否 则 根 据 t+[(N -t) mod(base- t)] 输 出 一 位 数 字 , 然 后 令 N = (N - t) div

  有一个通用可变长表示 法 。 Bootstring使 用 小 尾 序 列 , 因 此 堆 的 序 列 可 以 根 据 第 一 个 最 小 的 来 分 割 。

  t(j)的值根据常量 base、tmin、tmax, 以及变量 bias的值确定 :

  8

  GB/T 44596—2024

  如(t)(算(ba)s出来(e×)(的(j)j))小(-b)于iast,min,则令 t(j)=tmin ;如果计算出来的 t(j)大于 tmax,则令 t(j)=tmax。

  5.2.4 贝叶斯调解(Biasadaptation)

  每个堆被编码或解码后 ,下个堆的 bias值根据下列规则来计算 。

  a) 为了避免在下步操作中 ,数据溢出 ,堆会被按比例缩小 :

  b) 堆的增加补偿了下个(第二次及以后缩小时)堆,d会(e)lt入(e)lt更长(ad)iv的(2)字。符串里 :

  第一次缩小时 ,delta= delta div damp,其中 damp是个常数 ;

  数(de)l量(ta)括(e)l本(ta)(这(d)e个lta堆d和iv基(n)u码(po)位in的(ts))数,量(其)中)。numpoints是当前所有已编码的(或解码的) 码位

  c) 堆被不断的减小 ,直到它落入一个极限值(threshold) ,来预测表示下一个堆所需要的数字的数量 :

  while delta>((base-tmin) ×tmax)div2;

  d) Bias通过如下方式取得 :

  do letdelta= delta div(base-tmin) 。

  当前堆(b)ia隐(s)(了(b)a下(s)e个堆可能大小的信息(×在步骤 3中的除法)执,因此当 t(行次数))(j(+)) 等{[t(a)m(se)ax时(-t)m,i大(d)的(e)lt数(a)] 字(d)i(可(de)l能是(ta+)s倒(k)e} 后。

  一个数字 , 当 t(j)等于 tmin时 ,权重较小的数字有可能是倒数第三个数字 , 当 t(j) 介于 tmin和 tmax 时 ,相对应的数字有可能是倒数第二个数字 。

  5.3 Bootstring参数

  给定一个基础码位字符集 ,需要指定其中一个为分隔符 。base 的值不能大于所有剩余的可区分的基础码位的数量 。每个数字所代表的值为 0~ base-1变化 ,需要和可区分的非分隔符相联系 。在有些情况下 ,有些码位需要同样的数字值 ,如果基础码位不区分大小写 ,一个字母的大写和小写方式需要等效 。n 的初始值不能大 于 在 扩 展 字 串 中 最 小 的 非 基 础 码 位 。 剩 下 的 5 个 参 数 min,tmax, skew, damp, bias的初始值(initial_bias)应满足下列要求 :

  0<=tmin< =tmax< = base-1;

  只要上述要求满足 , 这 5 个 参 数 只 影 响 执 行 效 率 , 不 影 响 正 确 率 。 这 些 参 数 最 好 根 据 实 际 经 验确定 。

  ia(p)l>>_bia;;mod base< = base-tmin ;

  5.4 Punycode参数值

  Punycode使用 Bootstring的下列参数值 :

  tmin = 1;

  base = 36;

  tmax = 26;

  d(s)a(k)mp(ew) 70(38)0;;

  9

  initial(initial)_n(b)ias 12(72)8;=0x80;

  GB/T 44596—2024

  虽然 Punycode 对 输 入 数 字 的 唯 一 限 制 是 非 负 整 数 , 是 专 门 为 UNICODE 的 码 位 设 计 的 , UNICODE 的码位变化范围为 0~ 10FFFF(16进制) 。基础码位是 ASCII码位 0~ 7F(16进制) ,其中连字符号“-”(U+002D)是分隔符 ,其他码位的数值表示如下 :

  码位

  41. . 5A(A-Z)

  61. . 7A(a-z) 30. . 39(0-9)

  

  =

  =

  使用连字符号作为分隔符意味着 , 只有 UNICODE字串完全包含基础码位时 , 编码后的字串才有可能以连字符号结束 ,CDNA不准许只包含基础码位的字串进行编码 。CDNA会为编码后的字串增加一个前缀“xn-”。使用 Punycode 的 CDNA应遵守 RFC952 中关于主机名不能以连字符开始和结束的规定 。解码器应认出不区分大小写的字母 。编码器应输出只包含大小写其中一种方式的格式 。

  =

  

  数字值

  0~ 25,分别地

  0~ 25,分别地26~ 35,分别地

  5.5 Bootstring算法

  5.5. 1 贝叶斯调解函数

  满足一些条件的部分尾代码可被忽略 ,这些部分会用大括号括起来 ,并在后面加上一些注释 。码位就是整数 ,伪代码假设所有的数学操作能直接作用于码位 。在一些编程语言里 ,可能需要做一些码位跟整数之间的转换 。 附录 F提供了一个 C语言实现 Punycode算法的例子 。下面是贝叶斯调解函数的伪函数 :

  function adapt(delta,numpoints,firsttime) :

  else letdelta= delta div2

  iffirsttime then letdelta= delta div damp

  letk(letd)t0(a)= delta+(delta div numpoints)

  while delta>((base-tmin) *tmax)div2 do begin

  end

  letk(letd)tk(a)ba(de)s(lt)e(a)div(base-tmin)

  return k+(((base-tmin+1) * delta)div(delta+ skew))

  在 adapt()函数内 对 delta和 k 的 修 改 并 不 影 响 在 编 码 和 解 码 过 程 中同 名的 变 量 , 因 为 在 调 用adapt()函数后 ,调用者在读取前会先覆盖原来的值 。

  5.5.2 解码过程函数开始 :

  letlet(n)0(i)tial_n

  如果let(let)t一iiile(_)i之s前(tr)i存(ng)在in码(d)e位(x)e,把(df)r这(o)些(m)码(0),位复制到(序号从 0)o(开)utpu(始的)如(字)果(符)发(串)现非基础码位 ,则失败退

  出 。如果成功复制出的码位数大于零 ,则需要处理分隔符 。

  while 如果输入串 input中的字符没有被处理完 do begin。

  10

  letoldi= i

  GB/T 44596—2024

  letrk一1=个码位(BASE);,如;果(k+)理(eg)则(n)给出错误信息

  ifdigit

  或(或)码m-i位ka,(给(果)出(没)错(有)误(码)信(位)息(可)处)理 ,则给出错误信息)

  letn= n+idiv(length(output) +1) , (如果数值溢出则给出错误信息)

  letb(letw)ia(*)dpt(ba)s(i(e)-- d,i(,如length(果数)utp(溢)出,tto(误)信ldi息is) 0?)

  leti= imod[length(output) +1]

  {如果 n 是基础码位 ,则给出错误信息}

  在输出字串 output位置序号为 i 的地方插入 n

  增加 i 的值

  end

  函数结束

  如果 initial_n大于基础码位(在 punycode里 ,n 永远大于 initial_n) ,在括号里的关于检查 n 是否是基础码位的表述可以被忽略 。

  在给 t赋值时 ,如果 t为 tmin ~tmax,″+tmin″操作总是被忽略 。 当 bias

  5.5.3 编码过程函数开始 :

  如s(t)于(t)iiia把lii码位按顺序复制到(ut中的基础码位数)输出串 output中 ,并在串后加入分隔符

  {如果输入串 input中包含小于 n 的非基础码位 ,则给出错误信息}

  while h

  l串(de)l里(m)n个)码(*)(位(h)1(按),顺(序(如果)) dobeg(数值溢)i则给出错误信息)

  letm= 在输入串 input里 ,大于 n 的码位值最小的非基础码位的码位值

  letq= delta

  ifc< n {或者 c是基础码位} then 增加 delta的值 , (如果数值溢出则给出错误信息) ifc= = n then begin

  11

  GB/T 44596—2024

  或者 t= k-bias (k取其他值)

  fol(r)e(n(E)m;ifa(;)ifib(S)E{isgn,

  ifq

  为 digitt+((q-t) mod(base-t))输出码位

  end

  letq= (q-t)div(base-t)

  为数字 q 输出码位

  增加 h

  letdelt(letbias)a0(d)apt(delta,h+1,testh equals b?)

  end

  end

  增加 delta和 n

  end

  函数结束

  如果所有小于 initial_n 的码位是基础码位 ,在括号里的关于检查是否有非基础码位小于 n 的表述可被忽略 。 (在 punycode里 ,如果码位是无符号时 ,上述判断成立) 。

  在 punycode里 ,如果 initial_n大于所有基础码位 ,那么括号里关于基础码位和非基础码位的判断可被忽略 。在给 t赋 值 时 , 如 果 t为 tmin ~ tmax,″+tmin″操 作 总 是 被 忽 略 。 当 bias

  为了避免可能产生无效输出 ,应做溢出的检查 。 到外部循环最后时 , 因为 delta

  5.5.4 溢出处理

  对 CDNA,26bit的无符号整数已经足够处理所有的 CDNA 字段 ,任何大于 26 bit的堆都会超出UNICODE 的字符范围或者 CDNA字段长度限制 。 由于输入有可能不是有效的 CDNA 字段 , 因此溢出处理是必要的 。

  本文件建议在编码时 ,对输入进行检查 ,只输入符合 CDNA规定的字符和长度的字串 ,从而防止溢出 。在解码时 ,在 ToUnicode[CDNA]操作的范畴内进行解码 ,从而避免溢出 。

  12

  GB/T 44596—2024

  附 录 A

  (规范性)

  UNICODE保留字符表

  UNICODE 3. 2 中未分配的码位 。具体应符合 IETF RFC 3454中的表 A. 1。

  此表未分配码位第一个是 0221,最后一个是 E0080-EFFFD。

  13

  GB/T 44596—2024

  附 录 B (规范性)映射表

  映射表含有 3列 :

  — 映射源的码点 ;

  — 映射结果码点或者为空 ;

  — 映射的原因 。

  每列用分号隔开 。请注意第二列有可能为空 , 或者一个码点或者多个码点(多个码点之间用空格隔开) 。

  a) 普通的映射为空 :具体应符合 IETF RFC 3454中的表 B. 1;

  b) 使用 NFKC映射进行小写转化 :具体应符合 IETF RFC 3454中的表 B. 2;

  c) 非归一化所使用的小写映射 :具体应符合 IETF RFC 3454中的表 B. 3。

  14

  GB/T 44596—2024

  附 录 C (规范性)禁止输出表

  C. 1 禁止输出表

  本表每行列出一个禁止输出码点 。每行格式是 :禁止输出码点值 ,分号 ,码点名称作为注释 。

  C.2 空格

  C.2. 1 ASCII码的空格

  具体应符合 IETF RFC 3454中的表 C. 1. 1。

  C.2.2 非 ASCII码的空格

  具体应符合 IETF RFC 3454中的表 C. 1. 2。

  C.3 控制字符

  C.3. 1 ASCII码的控制字符

  C.3.2 非 ASCII码的控制字符

  具体应符合 IETF RFC 3454中的表 C. 2. 2。

  C.4 私用字符

  具体应符合 IETF RFC 3454中的表 C. 3。

  C.5 非字符码点

  具体应符合 IETF RFC 3454中的表 C. 4。

  C.6 代理码点

  具体应符合 IETF RFC 3454中的表 C. 5。

  C.7 与无格式字符不相称的

  具体应符合 IETF RFC 3454中的表 C. 6。

  15

  GB/T 44596—2024

  C. 8 与标准表示不相称的

  具体应符合 IETF RFC 3454中的表 C. 7。

  C.9 改变显示属性或者被 Unicode摒弃的

  具体应符合 IETF RFC 3454中的表 C. 8。

  C. 10 标签字符

  具体应符合 IETF RFC 3454中的表 C. 9。

  16

  GB/T 44596—2024

  附 录 D (规范性)双向字符表

  D. 1 带有标记“R”或“AL”的双向字符

  具体应符合 IETF RFC 3454中的表 D. 1。

  D.2 带有标记“L”的双向字符

  具体应符合 IETF RFC 3454中的表 D. 2。

  17

  GB/T 44596—2024

  附 录 E

  (资料性)

  Punycode编码解码例子

  E. 1 解码过程例子

  在下面的描述中 ,解码状态的变化通过一系列的十六进制的值反映出来 ,代表了扩展字串中的码位 。星号会正好出现在刚刚插入的码位后边 ,用来标识 n(星号前面的值)和 i(星号后面的值的位置) 。其他数值都是十进制值 。

  n 是 128,i是 0,bias是 72

  输入串是 "ihqwcrb4cv8a8dqg056pqjye"

  因为没有分隔符 ,所以扩展字串开头空

  串 "ihq"解码成 19853

  bias变成了 21

  4E0D*

  串 "wc"解码成 64

  bias变成了 20

  4E0D 4E2D*

  串 "rb"解码成 37

  bias变成了 13

  4E3A* 4E0D 4E2D

  串 "4c"解码成 56

  bias变成了 17

  4E3A 4E48* 4E0D 4E2D

  串 "v8a"解码成 599

  bias变成了 32

  4E3A 4EC0* 4E484E0D 4E2D

  串 "8d"解码成 130

  bias变成了 23

  4ED6* 4E3A 4EC04E484E0D 4E2D

  串 "qg"解码成 154

  bias变成了 25

  4ED64EEC* 4E3A 4EC04E484E0D 4E2D

  串 "056p"解码成 46301

  bias变成了 84

  4ED64EEC 4E3A 4EC04E484E0D 4E2D 6587*

  串 "qjye"解码成 88531

  bias变成了 90

  4ED64EEC 4E3A 4EC04E484E0D 8BF4* 4E2D 6587

  (他们为什么不说中文)

  18

  GB/T 44596—2024

  E.2 编码过程例子

  bias是 72

  输入串是 :

  4ED64EEC 4E3A 4EC04E484E0D 8BF44E2D 6587

  (注 : 这个字串对应的中文是 “他们为什么不说中文 ”)

  因为没有基本码位 ,所以没有不变的部分

  下一个需要输入的码位是 4E0D

  所需的堆的值是 19853,编码为 "ihq"

  bias变成了 21

  下一个需要输入的码位是 4E2D

  所需的堆的值是 64,编码为 "wc"

  bias变成了 20

  下一个需要输入的码位是 4E3A

  所需的堆的值是 37,编码为 "rb"

  bias变成了 13

  下一个需要输入的码位是 4E48

  所需的堆的值是 56,编码为 "4c"

  bias变成了 17

  下一个需要输入的码位是 4EC0

  所需的堆的值是 599,编码为 "v8a"

  bias变成了 32

  下一个需要输入的码位是 4ED6

  所需的堆的值是 130,编码为 "8d"

  bias变成了 23

  下一个需要输入的码位是 4EEC

  所需的堆的值是 154,编码为 "qg"

  bias变成了 25

  下一个需要输入的码位是 6587

  所需的堆的值是 46301,编码为 "056p"

  bias变成了 84

  下一个需要输入的码位是 8BF4

  所需的堆的值是 88531,编码为 "qjye"

  bias变成了 90

  输出串是 "ihqwcrb4cv8a8dqg056pqjye"

  19

  GB/T 44596—2024

  附 录 F

  (资料性)

  Punycode算法编码解码 C 语言实现例子

  下面用 C语言实现 Punycode算法的例子 ,供依据本文件实现 Punycode算法时作参考 ,本代码是Adam M. Costello实现的开源代码 。

  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

  * * * * * /

  /* Public interface(wouldnormallygoin its own .h file) :* /

  # include

  enum punycode_status {

  punycode_success,

  punycode_bad_ input, /* Input is invalid. * /

  punycode_big_output, /* Output would exceed the space provided.* /

  punycode_overflow /* Inputneeds wider integers to process. * / } ;

  # ifUINT_MAX> = (1<< 26) - 1

  typedef unsigned int punycode_uint;

  # else

  typedef unsigned long punycode_uint;

  # endif

  enum punycode_status punycode_encode(

  punycode_uint input_length,

  constpunycode_uint input[] ,

  const unsignedchar case_flags[] ,

  punycode_uint* output_length,

  char output[] ) ;

  /* punycode_encode()converts Unicode to Punycode. The input * /

  /* is represented as an arrayof Unicode code points(not code * /

  /* units; surrogate pairs are not allowed) ,and the output * /

  /* will be represented as an arrayof ASCII code points. The * /

  /* output string is* not* null-terminated; it will contain * /

  /* zeros if and only if the input contains zeros. (Of course * /

  /* the caller can leave room fora terminator and addone if * /

  /* needed.) The input_length is the number of code points in * /

  /* the input. The output_length is an in/out argument: the * /

  /* caller passes in the maximum number of code points that it * /

  20

  GB/T 44596—2024

  /* can receive,and on successful return it will contain the * /

  /* number of code points actuallyoutput. The case_flags array * /

  /* holds input_lengthboolean values,where nonzero suggests that* /

  /* the corresponding Unicode character be forced to uppercase * /

  /* after being decoded(ifpossible) ,and zero suggests that * /

  /* itbe forced to lowercase(ifpossible) . ASCII code points * /

  /* are encoded literally,except that ASCII letters are forced * /

  /* to uppercase or lowercase according to the corresponding * /

  /* uppercase flags. If case_flags is anull pointer then ASCII * /

  /* letters are left as theyare,and other code points are * /

  /* treated as if their uppercase flags were zero. The return * /

  /* value can be any of the punycode_ status values defined above * /

  /* exceptpunycode_bad_ input; ifnot punycode_ success,then * /

  /* output_ size and outputmight contain garbage. * /

  enum punycode_ status punycode_decode(

  punycode_uint input_length, const char input[] ,

  punycode_uint* output_length, punycode_uint output[] ,

  unsignedchar case_flags[] ) ;

  /* punycode_decode()converts Punycode to Unicode. The input is * /

  /* represented as an arrayof ASCII code points,and the output * /

  /* will be represented as an arrayof Unicode code points. The * /

  /* input_length is the number of code points in the input. The * /

  /* output_length is an in/out argument: the caller passes in * /

  /* the maximum number of code points that it can receive,and * /

  /* on successful return it will contain the actual number of * /

  /* code points output. The case_flags arrayneeds room for at * /

  /* least output_length values,or it can beanull pointer if the* /

  /* case information isnot needed. Anonzero flag suggests that * /

  /* the corresponding Unicode character be forced to uppercase * /

  /* bythe caller(ifpossible) ,while zero suggests that itbe * /

  /* forced to lowercase(ifpossible) . ASCII code points are * /

  /* output already in the proper case,but their flags will beset* /

  /* appropriately so that applying the flags wouldbe harmless. * /

  /* The return value can be any of the punycode_ status values * /

  /* defined above; ifnot punycode_ success,then output_length, * /

  /* output,and case_flags might contain garbage. On success,the* /

  /* decoder will never need to write an output_lengthgreater than* /

  /* input_length,because of how the encoding is defined. * /

  21

  GB/T 44596—2024

  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

  * * * /

  /* Implementation(wouldnormallygoin its own .c file) :* /

  # include< string.h>

  /* * * Bootstring parameters forPunycode* * * /

  enum { base=36,tmin= 1,tmax=26,skew=38,damp= 700,

  initial_bias= 72, initial_n= 0x80,delimiter= 0x2D } ;

  /* basic(cp)tests whether cp is a basic code point:* /

  # define basic(cp)((punycode_uint)(cp) < 0x80)

  /* delim(cp)tests whether cp is a delimiter:* /

  # definedelim(cp)((cp) = =delimiter)

  /* decode_digit(cp)returns the numeric value of a basic code* /

  /* point(for use in representing integers) in the range0 to* /

  /* base-1,orbase if cp is does not represent a value. * /

  static punycode_uint decode_digit(punycode_uintcp)

  {

  return cp- 48<10 ? cp- 22 : cp- 65<26? cp- 65 :

  cp-97<26? cp-97 : base; }

  /* encode_digit(d,flag)returns the basic code point whose value * /

  /* (when used forrepresenting integers)is d,which needs tobe in * /

  /* the range0 to base- 1. The lowercase form isused unless flag is * /

  /* nonzero, in which case the uppercase form isused. The behavior * /

  /* is undefined if flag is nonzero and digit dhasno uppercase form.* /

  static char encode_digit(punycode_uintd, int flag)

  {

  returnd+22+ 75* (d<26) - ((flag ! = 0) << 5) ;

  /* 0. .25map to ASCII a. .z orA. .Z* /

  /* 26. .35map to ASCII 0. .9 * /

  }

  /* flagged(bcp)tests whether a basic code point is flagged* /

  /* (uppercase) . The behavior is undefined ifbcp isnot a * /

  22

  GB/T 44596—2024

  /* basic code point. * /

  # define flagged(bcp)((punycode_uint)(bcp) - 65<26)

  /* encode_basic(bcp,flag)forces a basic code point to lowercase* / /* if flag is zero,uppercase if flag is nonzero,and returns * / /* the resulting code point. The code point is unchanged if it * / /* is caseless. The behavior is undefined ifbcp isnot a basic* / /* code point. * /

  static char encode_basic(punycode_uint bcp, int flag) {

  bcp - = (bcp-97<26) << 5;

  returnbcp+ ((! flag&&(bcp- 65<26)) << 5) ; }

  /* * * Platform- specific constants* * * /

  /* maxint isthe maximum value of apunycode_uint variable:* / static constpunycode_uint maxint= - 1;

  /* Because maxint is unsigned, - 1 becomes the maximum value.* /

  /* * * Bias adaptation function* * * /

  static punycode_uint adapt(

  punycode_uint delta,punycode_uint numpoints, int firsttime ) {

  punycode_uint k;

  delta= firsttime ? delta/damp : delta>> 1;

  /* delta>> 1 is a faster wayof doing delta/2* / delta+ =delta/numpoints;

  for(k= 0; delta>((base-tmin) * tmax)/ 2; k+ =base){ delta/=base-tmin;

  }

  returnk+ (base-tmin+ 1) * delta/(delta+ skew) ; }

  /* * * Main encode function* * * /

  23

  GB/T 44596—2024

  enum punycode_ status punycode_encode(

  punycode_uint input_length,

  constpunycode_uint input[] ,

  const unsignedchar case_flags[] ,

  punycode_uint* output_length,

  char output[] ) {

  punycode_uint n,delta,h,b,out,max_out,bias,j,m,q,k,t;

  /* Initialize the state:* /

  n= initial_n;

  delta=out= 0;

  max_out= * output_length;

  bias= initial_bias;

  /* Handle the basic code points:* /

  for(j = 0; j

  if(basic(input[j])){

  if(max_out-out<2)returnpunycode_big_output;

  output[out+ + ] =

  case_flags ? encode_basic(input[j] ,case_flags[j]) : input[j] ; }

  /* elseif(input[j]

  /* (not needed forPunycode with unsignedcode points) * / }

  h=b=out;

  /* his the number of code points that have been handled,b is the * / /* number of basic code points,andout isthe number of characters* / /* that have been output. * /

  if(b>0)output[out+ + ] =delimiter;

  /* Main encoding loop:* /

  while(h

  /* All non-basic code points

  /* handled already. Find the next larger one:* / for(m=maxint,j = 0; j

  24

  GB/T 44596—2024

  /* if(basic(input[j]))continue;* / /* (not needed forPunycode) * /

  if(input[j] > =n && input[j]

  /* Increase delta enoughto advance the decoders * / /* state to ,but guard against overflow:* /

  if(m-n)(maxint-delta)/(h+ 1))returnpunycode_overflow; delta+ = (m-n) * (h+ 1) ;

  n=m;

  for(j = 0; j

  /* Punycode does not need to check whether input[j] is basic:* / if(input[j]

  if( + +delta= = 0)returnpunycode_overflow;

  }

  if(input[j] = =n){

  /* Represent delta as a generalized variable- length integer:* /

  for(q=delta,k=base; ; k+ =base){

  if(out> =max_out)returnpunycode_big_output;

  t=k< =bias/* +tmin* / ? tmin : /* +tmin not needed* /

  k> =bias+tmax ? tmax : k-bias;

  if(q

  output[out+ + ] = encode_digit(t+ (q-t) % (base-t) ,0) ;

  q= (q-t)/(base-t) ; }

  output[out+ + ] = encode_digit(q,case_flags&&case_flags[j]) ;

  bias=adapt(delta,h+ 1,h= =b) ;

  delta= 0;

  + +h;

  }

  }

  + +delta, + +n; }

  * output_length=out;

  returnpunycode_ success;

  25

  GB/T 44596—2024

  }

  /* * * Main decode function* * * /

  enum punycode_ status punycode_decode( punycode_uint input_length,

  const char input[] ,

  punycode_uint* output_length,

  punycode_uint output[] ,

  unsignedchar case_flags[] ) {

  punycode_uint n,out, i,max_out,bias,

  b,j, in,oldi,w,k,digit,t; /* Initialize the state:* /

  n= initial_n;

  out= i= 0;

  max_out= * output_length;

  bias= initial_bias;

  /* Handle the basic code points: Letbbe the number of input code* / /* points before the last delimiter,or0 if there is none,then * / /* copythe firstb code points to the output. * /

  for(b= j = 0; j

  if(b>max_out)returnpunycode_big_output;

  for(j = 0; j

  if(case_flags) case_flags[out] = flagged(input[j]) ;

  if(! basic(input[j]))returnpunycode_bad_ input;

  output[out+ + ] = input[j] ;

  }

  /* Main decoding loop: Start just after the last delimiter if any * / /* basic code points were copied; start atthe beginning otherwise.* /

  for(in=b>0 ? b+ 1 : 0; in

  /* inis the indexof the next character tobe consumed,and* / /* out is the number of code points in the output array. * /

  26

  GB/T 44596—2024

  /* Decode ageneralized variable- length integer into delta, * / /* which gets added toi. The overflow checking is easier * /

  /* if we increase ias wego,then subtract off its starting* /

  /* value atthe end to obtain delta. * /

  for(oldi= i,w= 1,k=base; ; k+ =base){

  if(in> = input_length)returnpunycode_bad_ input; digit=decode_digit(input[in+ + ]) ;

  if(digit> =base) returnpunycode_bad_ input;

  if(digit>(maxint- i)/ w) returnpunycode_overflow;

  i+ =digit* w;

  t=k< =bias/* +tmin* / ? tmin : /* +tmin not needed* / k> =bias+tmax ? tmax : k-bias;

  if(digit

  if(w>maxint /(base-t))returnpunycode_overflow;

  w* = (base-t) ; }

  bias=adapt(i-oldi,out+ 1,oldi= = 0) ;

  /* i was supposed to wrap around fromout+ 1 to 0, * / /* incrementing n each time,so well fix thatnow:* /

  if(i/(out+ 1) > maxint-n) returnpunycode_overflow; n+ = i /(out+ 1) ;

  i % = (out+ 1) ;

  /* Insert natposition iof the output:* /

  /* not needed forPunycode:* /

  /* if(decode_digit(n) < =base) returnpunycode_ invalid_ input;* / if(out> =max_out)returnpunycode_big_output;

  if(case_flags){

  memmove(case_flags+ i+ 1,case_flags+ i,out- i) ;

  /* Case of last character determines uppercase flag:* / case_flags[i] = flagged(input[in- 1]) ;

  }

  memmove(output+ i+ 1,output+ i, (out- i) * sizeof* output) ;

  output[i+ + ] =n;

  27

  GB/T 44596—2024

  }

  * output_length=out;

  returnpunycode_ success; }

  /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

  * * * * * * * * * * * /

  /* Wrapper for testing(wouldnormallygo ina separate .c file) :* /

  # include< assert.h>

  # include< stdio.h>

  # include< stdlib.h>

  # include< string.h>

  /* For testing,well just set some compile-time limits rather than* /

  /* use malloc() ,and set a compile-time optionrather than using a * /

  /* command- line option. * /

  enum {

  unicode_max_length=256,

  ace_max_length=256 } ;

  static void usage(char* * argv) {

  fprintf(stderr,

  "/n"

  "% s - e reads code points and writes aPunycode string./n"

  "% s -dreads aPunycode string and writes code points./n"

  "/n"

  "Input and output are plaintext in the native character set./n"

  "Code points are in the form u+hex separatedby whitespace./n"

  "Althoughthe specification allows Punycode strings to contain/n"

  "any characters from the ASCII repertoire,this test code/n"

  "supports onlythe printable characters,and needs the Punycode/n"

  "string tobe followedby anewline./n"

  "The case of the u inu+hex is the force-to-uppercase flag./n"

  ,argv[0] ,argv[0]) ;

  exit(EXIT_FAILURE) ;

  }

  static void fail(const char* msg)

  28

  GB/T 44596—2024

  {

  fputs(msg,stderr) ;

  exit(EXIT_FAILURE) ; }

  static const chartoo_big[] =

  "input oroutput is too large,recompile withlarger limits/n"; static const char invalid_ input[] = "invalid input/n";

  static const char overflow[] = "arithmetic overflow/n";

  static const chario_error[] = "I/O error/n";

  /* The following string is used to convert printable* / /* characters between ASCII and the native charset: * /

  static const char print_ascii[] =

  "/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n"

  "/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n/n"

  "! /"# $ %&() * + , - ./"

  "0123456789: ;< = >? "

  "@ABCDEFGHIJKLMNO"

  "PQRSTUVWXYZ[//]^_ "

  "`abcdefghijklmno"

  "pqrstuvwxyz{ |} ~/n";

  int main(int argc,char* * argv)

  {

  enum punycode_ status status;

  intr;

  unsigned int input_length,output_length,j;

  unsignedchar case_flags[unicode_max_length] ;

  if(argc ! =2)usage(argv) ;

  if(argv[1][0] ! = - ) usage(argv) ;

  if(argv[1][2] ! = 0)usage(argv) ;

  if(argv[1][1] = =e){

  punycode_uint input[unicode_max_length] ;

  unsigned long codept;

  char output[ace_max_length+ 1] ,uplus[3] ;

  intc;

  /* Read the input code points:* /

  29

  GB/T 44596—2024

  input_length= 0;

  for( ; ;){

  r= scanf("%2s%lx",uplus,&codept) ;

  if(ferror(stdin))fail(io_error) ;

  if(r= =EOF ||r= = 0)break;

  if(r ! =2 ||uplus[1] ! = + ||codept>(punycode_uint) - 1){ fail(invalid_ input) ;

  }

  if(input_length= =unicode_max_length)fail(too_big) ;

  if(uplus[0] = =u) case_flags[input_length] = 0;

  elseif(uplus[0] = =U) case_flags[input_length] = 1;

  else fail(invalid_ input) ;

  input[input_length+ + ] =codept;

  }

  /* Encode:* /

  output_length=ace_max_length;

  status=punycode_encode(input_length, input,case_flags, &output_length,output) ;

  if(status= =punycode_bad_ input)fail(invalid_ input) ;

  if(status= =punycode_big_output)fail(too_big) ;

  if(status= =punycode_overflow)fail(overflow) ;

  assert(status= =punycode_ success) ;

  /* Convert to native charset and output:* /

  for(j = 0; j< output_length; + + j){

  c=output[j] ;

  assert(c> = 0 &&c< = 127) ;

  if(print_ascii[c] = = 0)fail(invalid_ input) ;

  output[j] =print_ascii[c] ;

  }

  output[j] = 0;

  r=puts(output) ;

  30

  GB/T 44596—2024

  if(r= =EOF)fail(io_error) ; returnEXIT_SUCCESS;

  }

  if(argv[1][1] = =d){

  char input[ace_max_length+2] ,* p,* pp;

  punycode_uint output[unicode_max_length] ;

  /* Read the Punycode input string and convert to ASCII:* /

  fgets(input,ace_max_length+2,stdin) ;

  if(ferror(stdin))fail(io_error) ;

  if(feof(stdin))fail(invalid_ input) ;

  input_length= strlen(input) - 1;

  if(input[input_length] ! = "/n")fail(too_big) ;

  input[input_length] = 0;

  for(p= input; * p ! = 0; + +p){

  pp= strchr(print_ascii,* p) ;

  if(pp= = 0)fail(invalid_ input) ;

  * p=pp-print_ascii; }

  /* Decode:* /

  output_length=unicode_max_length;

  status=punycode_decode(input_length, input,&output_length,

  output,case_flags) ;

  if(status= =punycode_bad_ input)fail(invalid_ input) ;

  if(status= =punycode_big_output)fail(too_big) ;

  if(status= =punycode_overflow)fail(overflow) ; assert(status= =punycode_ success) ;

  /* Output the result:* /

  for(j = 0; j< output_length; + + j){ r=printf("% s+ %04lX/n",

  case_flags[j] ? "U": "u",

  (unsigned long)output[j] ) ; if(r<0)fail(io_error) ;

  }

  31

  GB/T 44596—2024

  returnEXIT_SUCCESS; }

  usage(argv) ;

  returnEXIT_SUCCESS; /* not reached,but quiets compiler warning* / }

  32

29139484629
下载排行 | 下载帮助 | 下载声明 | 信息反馈 | 网站地图  360book | 联系我们谢谢