TypeScript-Int64实现

查了一些实现资料,找到以下几个Int64解决方案,整理起来。最后一个需要翻墙,直接把代码贴上,可以参考一下。

一、Javascript 的 64bit Int 支持

2个uint 拼接

这酸爽……

package lz.jprotoc
{import flash.utils.IDataInput;/*** ...* @author lizhi http://matrix3d.github.io/*/public class Int64 {public var low:uint = 0;public var high:uint = 0;public function Int64(low:uint=0,high:uint=0) {this.low = low;this.high = high;}public function equal(v:Int64):Boolean {if (v == null) return false;return (v.low == low) && (v.high == high);}public function isZero():Boolean {return low == 0 && high == 0;}public function toString():String {return "high:0x" + high.toString(16)+" low:0x" + low.toString(16);}}
}

虽然是 AS3 写的,但转成 JS 也是分分钟。

字符串拼接法

dom 同学用 ByteArray 来保存每个字节 (同样是 AS3),然后将其转成字符串来显示,缺点和上面 lizi 的一样,就是无法计算。

node-int64

node-int64 采用 Javascript 的 Number 来实现对超过 int32 的数值的保存。由于 Number 采用 双精度浮点数 来保存数值,因此该值的范围只能在 +/- 253 的范围内。

这是我最终的选择。因为金币的值在客户端是会参与计算的,但估计在游戏的有生之年都不可能大于 253 。

我基于该版本修改了一个 TypeScript 版的 Int64.ts,可以在 egret 中使用。

Number.isSafeInteger

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger

TishoYs Space 提到了使用 Number.isSafeInteger + parseInt 来处理 Int64,需要注意几个问题:

如果服务器传递过来的是数字,因为字节序的问题(2个4字节),必须使用上面提到的方法来读取;如果服务器传递过来的是字符串,那么可以使用 parseInt。
isSafeInteger 是在 ES6 加入的,在客户端要慎用。可以使用下面的代码自行实现 Number.isSafeInteger:

Number.isSafeInteger = Number.isSafeInteger || function (value) {return Number.isInteger(value) && Math.abs(value) <= Number.MAX_SAFE_INTEGER;
};

二、Int64.ts and Buffer.ts for Egret

前几天写了一篇 Javascript 的 64bit Int 支持,列举了一些在 Javascript 中支持 64bit 数值的已有方法。

其实,写那篇是为了在 egret 中支持 64bit 数值,原因么,上一篇有讲。

由于 egret 使用的是 TypeScript ,我基于 node-int64 翻译了一个 TypeScript 版本Int64.ts ,方便伸手党。同时为了方便和服务端大爷通信,又继承 egert.ByteArray 写了个 Buffer.ts 。

note-int64 采用的是 node 的 Buffer 来保存 64bit 数字信息。我给改成了使用 egret.ByteArray 。后来为了更加通用,又改成了直接使用 Array。

Buffer.ts 中则仅仅实现了 readInt64 和 writeInt64,Unsigned 版本直接调用这两个方法。

这两个文件都在 gist 上,请科学上网。

给一段测试代码:


var i64:Int64 = new Int64(0x1020304050607);
var buf:Buffer = new Buffer();
buf.writeInt64(i64);
buf.writeUnsignedInt64(i64.toNumber());
buf.position = 0;
for(var i:number=0;i<buf.length;i++)
{console.log(buf.readByte());
}
buf.position = 0;
console.log(buf.readInt64());
console.log(buf.readUnsignedInt64());
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 0
// 1
// 2
// 3
// 4
// 5
// 6
// 7
// 283686952306183
// 283686952306183

三、完整方案 链接需要科学上网。。

https://gist.github.com/zrong/6e8d6b733158b0539bf2#file-int64-ts

Buffer.ts


// Buffer.ts
// extend egret.ByteArray, implement writeInt64
// @author zrongzrong@gmail.com
// Creation 2015-09-14
class Buffer extends egret.ByteArray
{private static SIZE_OF_INT64:number = 8;private static SIZE_OF_UINT64:number = 8;constructor(buffer?:ArrayBuffer){super(buffer);}public readUnsignedInt64(raw=true):any {return this.readInt64(raw);}public writeUnsignedInt64(value:any):void {this.writeInt64(value);}public readInt64(raw=true):any {if (!this.validate(Buffer.SIZE_OF_INT64)) return null;var buffer:Array<number> = [];for(var i:number=0; i<Buffer.SIZE_OF_INT64; i++){buffer[i] = this.readByte();}var intValue:Int64 = new Int64(buffer);if(raw){return intValue.toNumber();}return intValue;}public writeInt64(value:any):void {var intValue:Int64;if(typeof(value) == 'number'){intValue = new Int64(value);}else{intValue = value;}var buffer:Array<number> = intValue.toBuffer(true);for(var i:number=0; i<buffer.length; i++){this.writeByte(buffer[i]);}}
}

Int64.js

//     Int64.js
//
//     Copyright (c) 2012 Robert Kieffer
//     MIT License - http://opensource.org/licenses/mit-license.php/*** Support for handling 64-bit int numbers in Javascript (node.js)** JS Numbers are IEEE-754 binary double-precision floats, which limits the* range of values that can be represented with integer precision to:** 2^^53 <= N <= 2^53** Int64 objects wrap a node Buffer that holds the 8-bytes of int64 data.  These* objects operate directly on the buffer which means that if they are created* using an existing buffer then setting the value will modify the Buffer, and* vice-versa.** Internal Representation** The internal buffer format is Big Endian.  I.e. the most-significant byte is* at buffer[0], the least-significant at buffer[7].  For the purposes of* converting to/from JS native numbers, the value is assumed to be a signed* integer stored in 2's complement form.** For details about IEEE-754 see:* http://en.wikipedia.org/wiki/Double_precision_floating-point_format*///
// Int64
//
class Int64
{// Useful masks and values for bit twiddlingpublic static MASK31:number =  0x7fffffff;public static VAL31:number = 0x80000000;public static MASK32:number =  0xffffffff;public static VAL32:number = 0x100000000;public static MAX_INT:number = Math.pow(2, 53);public static MIN_INT:number = -Math.pow(2, 53);private static _HEX:Array<any> = new Array<any>();public buffer:Array<number>;public offset:number;/*** Constructor accepts any of the following argument types:** new Int64(buffer[, offset=0]) - Existing Buffer with byte offset* new Int64(Uint8Array[, offset=0]) - Existing Uint8Array with a byte offset* new Int64(string)             - Hex string (throws if n is outside int64 range)* new Int64(number)             - Number (throws if n is outside int64 range)* new Int64(hi, lo)             - Raw bits as two 32-bit values*/public constructor(a1:any, a2?:any){this._buildHex();if (a1 instanceof Array){this.buffer = a1;this.offset = a2 || 0;}else if (Object.prototype.toString.call(a1) == '[object Uint8Array]'){// Under Browserify, Buffers can extend Uint8Arrays rather than an// instance of Buffer. We could assume the passed in Uint8Array is actually// a buffer but that won't handle the case where a raw Uint8Array is passed// in. We construct a new Buffer just in case.this.buffer = Array.apply([], a1);this.offset = a2 || 0;}else{this.buffer = this.buffer || [];this.offset = 0;this.setValue.apply(this, arguments);}}// Map for converting hex octets to stringsprivate _buildHex():void{//Int64._HEX = [];for (var i = 0; i < 256; i++) {Int64._HEX[i] = (i > 0xF ? '' : '0') + i.toString(16);}}/*** Do in-place 2's compliment.  See* http://en.wikipedia.org/wiki/Two's_complement*/private _2scomp(){var b = this.buffer, o = this.offset, carry = 1;for (var i = o + 7; i >= o; i--) {var v = (b[i] ^ 0xff) + carry;b[i] = v & 0xff;carry = v >> 8;}}/*** Set the value. Takes any of the following arguments:** setValue(string) - A hexidecimal string* setValue(number) - Number (throws if n is outside int64 range)* setValue(hi, lo) - Raw bits as two 32-bit values*/public setValue(hi:any, lo?:any):void {var negate:boolean = false;if (arguments.length == 1) {if (typeof(hi) == 'number') {// Simplify bitfield retrieval by using abs() value.  We restore sign// laternegate = hi < 0;hi = Math.abs(hi);lo = hi % Int64.VAL32;hi = hi / Int64.VAL32;if (hi > Int64.VAL32) throw new RangeError(hi  + ' is outside Int64 range');hi = hi | 0;} else if (typeof(hi) == 'string') {hi = (hi + '').replace(/^0x/, '');lo = hi.substr(-8);hi = hi.length > 8 ? hi.substr(0, hi.length - 8) : '';hi = parseInt(hi, 16);lo = parseInt(lo, 16);} else {throw new Error(hi + ' must be a Number or String');}}// Technically we should throw if hi or lo is outside int32 range here, but// it's not worth the effort. Anything past the 32'nd bit is ignored.// Copy bytes to buffervar b = this.buffer, o = this.offset;for (var i = 7; i >= 0; i--) {b[o+i] = lo & 0xff;lo = i == 4 ? hi : lo >>> 8;}// Restore sign of passed argumentif (negate) this._2scomp();}/*** Convert to a native JS number.** WARNING: Do not expect this value to be accurate to integer precision for* large (positive or negative) numbers!** @param allowImprecise If true, no check is performed to verify the* returned value is accurate to integer precision.  If false, imprecise* numbers (very large positive or negative numbers) will be forced to +/-* Infinity.*/public toNumber(allowImprecise:boolean=false):number {var b = this.buffer, o = this.offset;// Running sum of octets, doing a 2's complementvar negate = b[o] & 0x80, x = 0, carry = 1;for (var i = 7, m = 1; i >= 0; i--, m *= 256) {var v = b[o+i];// 2's complement for negative numbersif (negate) {v = (v ^ 0xff) + carry;carry = v >> 8;v = v & 0xff;}x += v * m;}// Return Infinity if we've lost integer precisionif (!allowImprecise && x >= Int64.MAX_INT) {return negate ? -Infinity : Infinity;}return negate ? -x : x;}/*** Convert to a JS Number. Returns +/-Infinity for values that can't be* represented to integer precision.*/public valueOf():number {return this.toNumber(false);}/*** Return string value** @param radix Just like Number#toString()'s radix*/public toString(radix:number=10):string {return this.valueOf().toString(radix);}/*** Return a string showing the buffer octets, with MSB on the left.** @param sep separator string. default is '' (empty string)*/public toOctetString(sep:string=''):string {var out = new Array(8);var b = this.buffer, o = this.offset;for (var i = 0; i < 8; i++) {out[i] = Int64._HEX[b[o+i]];}return out.join(sep || '');}/*** Returns the int64's 8 bytes in a buffer.** @param {bool} [rawBuffer=false]  If no offset and this is true, return the internal buffer.  Should only be used if*                                  you're discarding the Int64 afterwards, as it breaks encapsulation.*/public toBuffer(rawBuffer:boolean=false):Array<number> {if (rawBuffer && this.offset === 0) return this.buffer;var out = Array.call([], this.buffer);return out;}/*** Returns a number indicating whether this comes before or after or is the* same as the other in sort order.** @param {Int64} other  Other Int64 to compare.*/public compare(other:Int64):number {// If sign bits differ ...if ((this.buffer[this.offset] & 0x80) != (other.buffer[other.offset] & 0x80)) {return other.buffer[other.offset] - this.buffer[this.offset];}// otherwise, compare bytes lexicographicallyfor (var i = 0; i < 8; i++) {if (this.buffer[this.offset+i] !== other.buffer[other.offset+i]) {return this.buffer[this.offset+i] - other.buffer[other.offset+i];}}return 0;}/*** Returns a boolean indicating if this integer is equal to other.** @param {Int64} other  Other Int64 to compare.*/public equals(other:Int64):boolean {return this.compare(other) === 0;}/*** Pretty output in console.log*/public inspect():string {return '[Int64 value:' + this + ' octets:' + this.toOctetString(' ') + ']';}
}

参考

  • https://blog.zengrong.net/post/2363.html
  • https://blog.zengrong.net/post/2367.html
  • https://gist.github.com/zrong/6e8d6b733158b0539bf2#file-int64-ts 感谢作者!

TypeScript-Int64实现相关推荐

  1. 超简单的react和typescript和引入scss项目搭建流程

    1.首先我们先创建一个react项目,react官网也有react项目搭建的命令 npx create-react-app my-app cd my-app 2.安装我们项目需要的样式依赖,这个项目我 ...

  2. golang int64转string_Golang 并发数据冲突检测器与并发安全

    介绍 共享数据竞争问题是并发系统中常见且难排查的问题. 什么是数据竞争? 当两个协程goroutine同时访问相同的共享变量,其中有一个执行了写操作,或两个都执行了写操作,就会出现数据竞争问题,导致数 ...

  3. 老码农绝密:使用 TS(TypeScript) 的 10 大理由

    最近,小编读了一篇名为<放弃 TypeScript 的 7 个非常好的理由>,这篇文章的阅读量不低.里面有些观点确实有趣,不过在这里我要向你介绍使用 TypeScript 的 10 个理由 ...

  4. 设置WebStorm像VSCode一样每行代码结尾自动格式化加入“;”分号(JavaScript、TypeScript格式化)

    Ctrl+Shift+S→编辑器→Code Style→JavaScript或TypeScript→Punctuation 1.每行代码结尾自动加上;分号: Use(下拉框选Use)semiconlo ...

  5. 为TypeScript项目生成API文档

    为TypeScript项目生成文档 使用typedoc为TypeScript项目生成API文档. 1. 使用typedoc生成HTML文档 需要安装 typedoc. npm i typedoc 可以 ...

  6. TypeScript 1

    TypeScript 的由来 TypeScript 是 JavaScript 的一个超集,支持 ECMAScript 6 标准. TypeScript 由微软开发的自由和开源的编程语言. TypeSc ...

  7. TypeScript学习笔记之 接口(Interface)

    在java中,接口是用来定义一些规范,使用这些接口,就必须实现接口中的方法,而且接口中的属性必须是常量. javascript中是没有接口的概念的.所以TypeScript在编译成 JavaScrip ...

  8. c# typescript_在任何IDE中从C#,Java或Python代码获取TypeScript接口的简单方法

    c# typescript by Leonardo Carreiro 莱昂纳多·卡雷罗(Leonardo Carreiro) 在任何IDE中从C#,Java或Python代码获取TypeScript接 ...

  9. 如何在React中使用Typescript

    TypeScript can be very helpful to React developers. TypeScript对React开发人员可能非常有帮助. In this video, Ben ...

  10. 使用Typescript的巧妙React上下文技巧-不是Redux

    by Bill Girten 比尔·吉尔滕(Bill Girten) 使用Typescript的巧妙React上下文技巧- 不是 Redux (Clever React context tricks ...

最新文章

  1. 《The C Programming Language》(2nd Ed) Introduction 翻译
  2. ArcGIS客户端开发学习笔记(五)——ArcGIS REST API基础
  3. php foreach 循环 判断index 小于多少_Go 与 PHP 的语法对比
  4. 怎么辨别iPhone手机的真伪?
  5. [转]PDB——Python调试利器详解
  6. 巨人网络305亿并购海外棋牌类游戏公司审核遭暂停
  7. SQL——后台分页(C#,mysql)
  8. PHP设计模式——职责链模式
  9. 解决button多次重复点击
  10. 网页英文 错位_网页错位原因解决方法
  11. U盘安装WIN10移动系统
  12. win7更新_今天,Win7正式终止更新,扫雷成为历史
  13. 邱天计算机,华北电力大学控制与计算机工程学院导师教师师资介绍简介-邱天...
  14. Linux下简单编译so库,调用另一个so库的方法
  15. java项目设计与思路
  16. 学计算机用苹果电脑号码,怎么用苹果电脑进行公众号排版
  17. ActiveMQ从入门到精通(全)
  18. 计算机校本培训心得,校本培训心得体会总结
  19. 最新(U盘木马)Auto病毒专杀
  20. mysql存储过程语法大全

热门文章

  1. C#数据类型转换,电工专用
  2. 教你@media媒体查询来适配ipad iphone5678plus 各种屏幕
  3. 四元数和欧拉角的yaw转换
  4. Linux基本运维汇总
  5. 信号处理(3)——调制
  6. MySQL查询每个部门的员工个数(部门员工数可能为0)
  7. 大型企业云平台构建-从大集中化再到分布式单元网格
  8. Activiti 历史任务查询
  9. kb和KB的关系,k/K表示1000还是1024
  10. 【解决Chrome浏览器和IE浏览器上传附件兼容的问题 -- Chrome关闭flash后,uploadify插件不可用的解决办法】