Mercurial > core / lisp/lib/net/codec/punycode.lisp
changeset 698: |
96958d3eb5b0 |
parent: |
524dfb768c7a
|
author: |
Richard Westhaver <ellis@rwest.io> |
date: |
Fri, 04 Oct 2024 22:04:59 -0400 |
permissions: |
-rw-r--r-- |
description: |
fixes |
1 ;;; lib/net/codec/punycode.lisp --- RFC 3492 Punycode strings 3 ;; This library was written by Yukari Hafner <shinmera@tymoon.eu>: https://github.com/Shinmera/punycode.git 6 (in-package :net/codec/punycode) 8 (defconstant INITIAL-N #x80) 9 (defconstant INITIAL-BIAS 72) 14 (defconstant DAMP 700) 16 (defun encode-digit (code) 17 (code-char (+ code (if (< code 26) 97 22)))) 19 (defun adapt (delta num-points first-time-p) 20 (setf delta (if first-time-p 23 (incf delta (truncate delta num-points)) 24 (loop for k from 0 by BASE 25 while (< (ash (* TMAX (- BASE TMIN)) -1) delta) 26 do (setf delta (truncate delta (- BASE TMIN))) 27 finally (return (+ k (truncate (* delta (+ 1 (- BASE TMIN))) (+ delta SKEW)))))) 29 (defmacro with-stream (stream-ish &body body) 30 (let ((thunk (gensym "THUNK"))) 31 `(flet ((,thunk (,stream-ish) 35 (with-output-to-string (,stream-ish) 36 (,thunk ,stream-ish))) 38 (,thunk *standard-output*)) 40 (,thunk ,stream-ish)))))) 42 (defun encode-punycode (string &optional out) 48 (loop for i from 0 below (length string) 49 for char = (char string i) 50 for code = (char-code char) 56 (loop with handled = basic 57 for m = most-positive-fixnum 58 for handled+1 = (1+ handled) 59 while (< handled (length string)) 60 do (loop for char across string 61 for code = (char-code char) 62 do (when (<= n code (1- m)) 64 (incf delta (* (- m n) handled+1)) 66 (loop for char across string 67 for code = (char-code char) 72 (loop for k from BASE by BASE 73 for tt = (cond ((<= k bias) TMIN) 74 ((<= (+ bias TMAX) k) TMAX) 76 do (when (< q tt) (return)) 77 (write-char (encode-digit (+ tt (mod (- q tt) (- BASE tt)))) out) 78 (setf q (truncate (- q tt) (- BASE tt)))) 79 (write-char (encode-digit q) out) 80 (setf bias (adapt delta handled+1 (= handled basic))) 86 (defun decode-digit (char) 87 (let ((code (char-code char))) 88 (cond ((<= #x30 code #x39) (+ 26 (- code #x30))) 89 ((<= #x41 code #x5A) (- code #x41)) 90 ((<= #x61 code #x7A) (- code #x61)) 93 (defun decode-punycode (string &optional out) 97 (basic (or (position #\- string :from-end T) 0)) 99 (uni (make-array (length string) :element-type 'character))) 100 ;; This is gross, I know. But we can't stream things out nicely because 101 ;; later mixed codepoints can have the same target index, causing earlier 102 ;; codepoints to be shifted downwards, which we obviously cannot do if we 103 ;; already emitted the codepoint to stream. So we instead copy to a string 105 (loop for i from 0 below basic 106 do (setf (char uni i) (char string i))) 107 (flet ((insert (pos char) 108 (loop for i downfrom written above pos 109 do (setf (char uni i) (char uni (1- i)))) 110 (setf (char uni pos) char))) 111 (loop with in = (if (< 0 basic) (1+ basic) 0) 113 while (< in (length string)) 115 for k from BASE by BASE 116 for digit = (decode-digit (char string in)) 119 (let ((tt (cond ((<= k bias) TMIN) 120 ((<= (+ bias TMAX) k) TMAX) 122 (when (< digit tt) (return)) 123 (setf w (* w (- base tt))))) 125 (setf bias (adapt (- i old-i) written (= old-i 0))) 126 (incf n (truncate i written)) 127 (setf i (mod i written)) 128 (insert i (code-char n)) 131 (write-string uni out :end written)))) 133 (defun encode-domain (string &optional out) 135 (loop for start = 0 then (1+ end) 136 for end = (or (position #\. string :start start) (length string)) 137 do (cond ((loop for i from start below end 138 thereis (< 127 (char-code (char string i)))) 139 (write-string "xn--" out) 140 (encode-punycode (subseq string start end) out)) 142 (write-string string out :start start :end end))) 143 (if (< end (length string)) 147 (defun decode-domain (string &optional out) 149 (loop for start = 0 then (1+ end) 150 for end = (or (position #\. string :start start) (length string)) 151 do (cond ((and (< (length "xn--") (- end start)) 152 (string= "xn--" string :start2 start :end2 (+ start (length "xn--")))) 153 (decode-punycode (subseq string (+ start (length "xn--")) end) out)) 155 (write-string string out :start start :end end))) 156 (if (< end (length string)) 160 (eval-when (:load-toplevel) 161 (push :rfc3492 *features*))