license import * as i18n from "../ast/i18n_ast" ; export function digest ( message ) { return message . id || sha1 ( serializeNodes ( message . nodes ) . join ( "" ) + `[${ message . meaning }]` ) ; } export function decimalDigest ( message ) { if ( message . id ) { return message . id ; } const visitor = new SerializerIgnoreIcuExpVisitor ( ) ; const parts = message . nodes . map ( a => a . visit ( visitor , null ) ) ; return computeMsgId ( parts . join ( "" ) , message . meaning ) ; } internal class SerializerVisitor implements i18n . Visitor { visitText ( text , context ) { return text . value ; } visitContainer ( container , context ) { return `[${ container . children . map ( child => child . visit ( this ) ) . join ( ", " ) }]` ; } visitIcu ( icu , context ) { const strCases = Object . keys ( icu . cases ) . map ( ( k ) => `${ k } {${ icu . cases [ k ] . visit ( this ) }}` ) ; return `{${ icu . expression }, ${ icu . type }, ${ strCases . join ( ", " ) }}` ; } visitTagPlaceholder ( ph , context ) { return ph . isVoid ? `<ph tag name="${ ph . startName }"/>` : `<ph tag name="${ ph . startName }">${ ph . children . map ( child => child . visit ( this ) ) . join ( ", " ) }</ph name="${ ph . closeName }">` ; } visitPlaceholder ( ph , context ) { return ph . value ? `<ph name="${ ph . name }">${ ph . value }</ph>` : `<ph name="${ ph . name }"/>` ; } visitIcuPlaceholder ( ph , context ? ) { return `<ph icu name="${ ph . name }">${ ph . value . visit ( this ) }</ph>` ; } } const serializerVisitor = new SerializerVisitor ( ) ; export function serializeNodes ( nodes ) { return nodes . map ( a => a . visit ( serializerVisitor , null ) ) ; } internal class SerializerIgnoreIcuExpVisitor extends SerializerVisitor { visitIcu ( icu , context ) { const strCases = Object . keys ( icu . cases ) . map ( ( k ) => `${ k } {${ icu . cases [ k ] . visit ( this ) }}` ) ; return `{${ icu . type }, ${ strCases . join ( ", " ) }}` ; } } /**
 * Compute the SHA1 of the given string
 *
 * see http://csrc.nist.gov/publications/fips/fips180-4/fips-180-4.pdf
 *
 * WARNING: this function has not been designed not tested with security in mind.
 *          DO NOT USE IT IN A SECURITY SENSITIVE CONTEXT.
 */ export function sha1 ( str ) { const utf8 = utf8Encode ( str ) ; const words32 = stringToWords32 ( utf8 , Endian . Big ) ; const len = utf8 . length * 8 ; const w = new Array ( 80 ) ; let [ a , b , c , d , e ] = [ 0x67452301 , 0xefcdab89 , 0x98badcfe , 0x10325476 , 0xc3d2e1f0 ] ; words32 [ len >> 5 ] |= 0x80 << ( 24 - len % 32 ) ; words32 [ ( ( ( len + 64 ) >> 9 ) << 4 ) + 15 ] = len ; for ( let i = 0 ; i < words32 . length ; i += 16 ) { const [ h0 , h1 , h2 , h3 , h4 ] = [ a , b , c , d , e ] ; for ( let j = 0 ; j < 80 ; j ++ ) { if ( j < 16 ) { w [ j ] = words32 [ i + j ] ; } else { w [ j ] = rol32 ( w [ j - 3 ] ^ w [ j - 8 ] ^ w [ j - 14 ] ^ w [ j - 16 ] , 1 ) ; } const [ f , k ] = fk ( j , b , c , d ) ; const temp = [ rol32 ( a , 5 ) , f , e , k , w [ j ] ] . reduce ( add32 ) ; [ e , d , c , b , a ] = [ d , c , rol32 ( b , 30 ) , a , temp ] ; } [ a , b , c , d , e ] = [ add32 ( a , h0 ) , add32 ( b , h1 ) , add32 ( c , h2 ) , add32 ( d , h3 ) , add32 ( e , h4 ) ] ; } return byteStringToHexString ( words32ToByteString ( [ a , b , c , d , e ] ) ) ; } function fk ( index , b , c , d ) { if ( index < 20 ) { return [ ( b & c ) | ( ~ b & d ) , 0x5a827999 ] ; } if ( index < 40 ) { return [ b ^ c ^ d , 0x6ed9eba1 ] ; } if ( index < 60 ) { return [ ( b & c ) | ( b & d ) | ( c & d ) , 0x8f1bbcdc ] ; } return [ b ^ c ^ d , 0xca62c1d6 ] ; } /**
 * Compute the fingerprint of the given string
 *
 * The output is 64 bit number encoded as a decimal string
 *
 * based on:
 * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
 */ export function fingerprint ( str ) { const utf8 = utf8Encode ( str ) ; let [ hi , lo ] = [ hash32 ( utf8 , 0 ) , hash32 ( utf8 , 102072 ) ] ; if ( hi === 0 && ( lo === 0 || lo === 1 ) ) { hi = hi ^ 0x130f9bef ; lo = lo ^ - 0x6b5f56d8 ; } return [ hi , lo ] ; } export function computeMsgId ( msg , meaning ) { let [ hi , lo ] = fingerprint ( msg ) ; if ( meaning ) { const [ him , lom ] = fingerprint ( meaning ) ; [ hi , lo ] = add64 ( rol64 ( [ hi , lo ] , 1 ) , [ him , lom ] ) ; } return byteStringToDecString ( words32ToByteString ( [ hi & 0x7fffffff , lo ] ) ) ; } function hash32 ( str , c ) { let [ a , b ] = [ 0x9e3779b9 , 0x9e3779b9 ] ; let i ; const len = str . length ; for ( i = 0 ; i + 12 <= len ; i += 12 ) { a = add32 ( a , wordAt ( str , i , Endian . Little ) ) ; b = add32 ( b , wordAt ( str , i + 4 , Endian . Little ) ) ; c = add32 ( c , wordAt ( str , i + 8 , Endian . Little ) ) ; [ a , b , c ] = mix ( [ a , b , c ] ) ; } a = add32 ( a , wordAt ( str , i , Endian . Little ) ) ; b = add32 ( b , wordAt ( str , i + 4 , Endian . Little ) ) ; c = add32 ( c , len ) ; c = add32 ( c , wordAt ( str , i + 8 , Endian . Little ) << 8 ) ; return mix ( [ a , b , c ] ) [ 2 ] ; } function mix ( [ a , b , c ] ) { a = sub32 ( a , b ) ; a = sub32 ( a , c ) ; a ^= c >>> 13 ; b = sub32 ( b , c ) ; b = sub32 ( b , a ) ; b ^= a << 8 ; c = sub32 ( c , a ) ; c = sub32 ( c , b ) ; c ^= b >>> 13 ; a = sub32 ( a , b ) ; a = sub32 ( a , c ) ; a ^= c >>> 12 ; b = sub32 ( b , c ) ; b = sub32 ( b , a ) ; b ^= a << 16 ; c = sub32 ( c , a ) ; c = sub32 ( c , b ) ; c ^= b >>> 5 ; a = sub32 ( a , b ) ; a = sub32 ( a , c ) ; a ^= c >>> 3 ; b = sub32 ( b , c ) ; b = sub32 ( b , a ) ; b ^= a << 10 ; c = sub32 ( c , a ) ; c = sub32 ( c , b ) ; c ^= b >>> 15 ; return [ a , b , c ] ; } enum Endian { Little , Big } function add32 ( a , b ) { return add32to64 ( a , b ) [ 1 ] ; } function add32to64 ( a , b ) { const low = ( a & 0xffff ) + ( b & 0xffff ) ; const high = ( a >>> 16 ) + ( b >>> 16 ) + ( low >>> 16 ) ; return [ high >>> 16 , ( high << 16 ) | ( low & 0xffff ) ] ; } function add64 ( [ ah , al ] , [ bh , bl ] ) { const [ carry , l ] = add32to64 ( al , bl ) ; const h = add32 ( add32 ( ah , bh ) , carry ) ; return [ h , l ] ; } function sub32 ( a , b ) { const low = ( a & 0xffff ) - ( b & 0xffff ) ; const high = ( a >> 16 ) - ( b >> 16 ) + ( low >> 16 ) ; return ( high << 16 ) | ( low & 0xffff ) ; } function rol32 ( a , count ) { return ( a << count ) | ( a >>> ( 32 - count ) ) ; } function rol64 ( [ hi , lo ] , count ) { const h = ( hi << count ) | ( lo >>> ( 32 - count ) ) ; const l = ( lo << count ) | ( hi >>> ( 32 - count ) ) ; return [ h , l ] ; } function stringToWords32 ( str , endian ) { const words32 = Array ( ( str . length + 3 ) >>> 2 ) ; for ( let i = 0 ; i < words32 . length ; i ++ ) { words32 [ i ] = wordAt ( str , i * 4 , endian ) ; } return words32 ; } function byteAt ( str , index ) { return index >= str . length ? 0 : str . charCodeAt ( index ) & 0xff ; } function wordAt ( str , index , endian ) { let word = 0 ; if ( endian === Endian . Big ) { for ( let i = 0 ; i < 4 ; i ++ ) { word += byteAt ( str , index + i ) << ( 24 - 8 * i ) ; } } else { for ( let i = 0 ; i < 4 ; i ++ ) { word += byteAt ( str , index + i ) << ( 8 * i ) ; } } return word ; } function words32ToByteString ( words32 ) { return words32 . reduce ( ( str , word ) => str + word32ToByteString ( word ) , "" ) ; } function word32ToByteString ( word ) { let str = "" ; for ( let i = 0 ; i < 4 ; i ++ ) { str += String . fromCharCode ( ( word >>> ( 8 * ( 3 - i ) ) ) & 0xff ) ; } return str ; } function byteStringToHexString ( str ) { let hex = "" ; for ( let i = 0 ; i < str . length ; i ++ ) { const b = byteAt ( str , i ) ; hex += ( b >>> 4 ) . toString ( 16 ) + ( b & 0x0f ) . toString ( 16 ) ; } return hex . toLowerCase ( ) ; } function byteStringToDecString ( str ) { let decimal = "" ; let toThePower = "1" ; for ( let i = str . length - 1 ; i >= 0 ; i -- ) { decimal = addBigInt ( decimal , numberTimesBigInt ( byteAt ( str , i ) , toThePower ) ) ; toThePower = numberTimesBigInt ( 256 , toThePower ) ; } return decimal . split ( "" ) . reverse ( ) . join ( "" ) ; } function addBigInt ( x , y ) { let sum = "" ; const len = Math . max ( x . length , y . length ) ; for ( let i = 0 , carry = 0 ; i < len || carry ; i ++ ) { const tmpSum = carry + + ( x [ i ] || 0 ) + + ( y [ i ] || 0 ) ; if ( tmpSum >= 10 ) { carry = 1 ; sum += tmpSum - 10 ; } else { carry = 0 ; sum += tmpSum ; } } return sum ; } function numberTimesBigInt ( num , b ) { let product = "" ; let bToThePower = b ; for ( ; num !== 0 ; num = num >>> 1 ) { if ( num & 1 ) { product = addBigInt ( product , bToThePower ) ; } bToThePower = addBigInt ( bToThePower , bToThePower ) ; } return product ; } function utf8Encode ( str ) { let encoded = "" ; for ( let index = 0 ; index < str . length ; index ++ ) { let codePoint = str . charCodeAt ( index ) ; if ( codePoint >= 0xd800 && codePoint <= 0xdbff && str . length > index + 1 ) { const low = str . charCodeAt ( index + 1 ) ; if ( low >= 0xdc00 && low <= 0xdfff ) { index ++ ; codePoint = ( ( codePoint - 0xd800 ) << 10 ) + low - 0xdc00 + 0x10000 ; } } if ( codePoint <= 0x7f ) { encoded += String . fromCharCode ( codePoint ) ; } else if ( codePoint <= 0x7ff ) { encoded += String . fromCharCode ( ( ( codePoint >> 6 ) & 0x1f ) | 0xc0 , ( codePoint & 0x3f ) | 0x80 ) ; } else if ( codePoint <= 0xffff ) { encoded += String . fromCharCode ( ( codePoint >> 12 ) | 0xe0 , ( ( codePoint >> 6 ) & 0x3f ) | 0x80 , ( codePoint & 0x3f ) | 0x80 ) ; } else if ( codePoint <= 0x1fffff ) { encoded += String . fromCharCode ( ( ( codePoint >> 18 ) & 0x07 ) | 0xf0 , ( ( codePoint >> 12 ) & 0x3f ) | 0x80 , ( ( codePoint >> 6 ) & 0x3f ) | 0x80 , ( codePoint & 0x3f ) | 0x80 ) ; } } return encoded ; }