VSCode+NWJS调试环境配置

我使用的Mac OS系统。 需要说明的是: Debugger For NWjs 可以一键式配置,但是nwjs官网速度实在是惨不忍睹啊。 首先,需要下载 nwjs sdk,官网速度能忍就忍吧,不能忍就下载国内的镜像版本,可以从这里下载https://nwjs.org.cn/download.html VSCode是必需要安装的。 说下调试配置。先在VSCode的插件市场中下载安装 Debugger For NWjs, 然后,在系统的用户目录下建立如下目录: # ~/.nwjs/nwjs-sdk-版本-系统平台-cpu架构 # 例如 mkdir ~/.nwjs/nwjs-sdk-v0.43.1-osx-x64 然后把下载的 nwjs sdk 解压到这个目录下,注意需要nwjs就在这个一级目录下。 然后用VSCode打开nwjs项目, VSCode ->调试->添加配置,应该就可以调试了。

December 31, 2019 · 1 min · holdsky

在Ubuntu19.10 上部署dzzoffice+onlyoffice协同办公系统

单台计算机的安装部署过程。 硬件要求(推荐) 一台计算机 CPU 2G+,实测1G也可以运行,但仍然建议大点吧 RAM 2G+,实测1G也可以运行,但仍然建议大点吧 硬盘可用空间,实测大于5G就可以安装部署,但仍然建议越大越好 独立网卡和高速网络 固定ip地址 需要的软件 Ubuntu19.10 server版 Docker Apache2 php7 MySql dzzoffice 安装部署过程 Ubuntu19.10 server 官方的太慢了,用国内镜像吧,我用的是网易镜像站 下载地址: http://mirrors.163.com/ubuntu-releases/19.10/ubuntu-19.10-live-server-amd64.iso 下载完成后,直接安装,安装过程中需要联网,ubuntu安装向导挺人性化的,接近Windows系统安装,很容易很方便。 需要注意的是,ubuntu 安装过程中有一步骤需要输入镜像站地址,因为众所周知的原因,还是输入国内的吧。我输入的是网易的, http://mirrors.163.com/ubuntu (建议)安装OpenSSH Server 这步骤不是必需的,但会方便远程访问,更方便后续安装过程的复制粘贴。 sudo apt-get install openssh-server #启动ssh服务 sudo service ssh start 然后远程登陆主机 我使用VSCode + Remote SSH。之所以远程登陆,是为了方便后面的复制粘贴。 VSCode + Remote SSH大概配置如下: Host dzz_ubuntu19.10 HostName ubuntu主机地址(可以是ip地址) User 用户名 # Port 22 默认是22 上面配置是使用用户名和密码的方式访问,也可以使用私钥文件方式访问,本文不讨论。 安装Docker 安装过程,参考https://www.runoob.com/docker/ubuntu-docker-install.html 如果有旧版本,先卸载旧版本 $ sudo apt-get remove docker docker-engine docker.io containerd runc 更新索引 ...

December 30, 2019 · 2 min · holdsky

局域网部署文档协同办公系统:Windows + onlyoffice + dzzoffice

如果能连接公网的话,关闭本页面吧。一大票的好用的解决方案、office365、google doc、wps、腾讯文档、石墨文档。。。。。。 废话不说,直接进入正题,单台计算机的安装部署过程。 硬件要求 一台计算机 CPU 2G+ RAM 4G+ 独立网卡和高速网络 固定ip地址 需要的软件 Windows操作系统,Windows7之类,不需要Windows Server也可以 Virtual Box,虚拟机 DockerToolBox,docker集成工具 Xampp,LAMP集成环境 Onlyoffce document server dzzoffice 安装部署过程 安装VirtualBox 官方下载地址: https://www.virtualbox.org/wiki/Downloads 国内下载地址:自己百度吧。国内下载的速度会快一些 下载完成后,直接安装。在Windows上安装软件基本可以“一路确定” 安装Docker Windows上使用DockerToolBox集成工具,国内可以使用阿里云的镜像来下载,下载地址: http://mirrors.aliyun.com/docker-toolbox/windows/docker-toolbox/ 同样是“一路确定”。详细安装过程,参考https://www.runoob.com/docker/windows-docker-install.html 安装后,启动 Docker Quickstart Terminal 初始化Docker,等待即可(中途可能会询问权限,放开就好)。 用Docker安装Onlyoffce document server 打开 Docker Quickstart Terminal,正常的情况下会看到这么一行 docker is configured to use the default machine with IP 192.168.99.100 记下这个ip地址,后面会用到。 输入命令 docker pull onlyoffice/documentserver 等待吧,如果网速不快也许就半个小时。 安装后,启动documentserver,并将宿主9000端口映射到docker的80端口 docker run -i -t -d -p 9000:80 onlyoffice/documentserver 用浏览器打开 http://ip:9000(本文对应 http://192.168.99.100:9000),应能看到 ...

December 28, 2019 · 1 min · holdsky

JavaScript检测是否支持WebP图片(出自Google官方文档)

function check_webp_feature(feature, callback) { var kTestImages = { lossy: "UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA", lossless: "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==", alpha: "UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==", animation: "UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA" }; var img = new Image(); img.onload = function () { var result = (img.width > 0) && (img.height > 0); callback(feature, result); }; img.onerror = function () { callback(feature, false); }; img.src = "data:image/webp;base64," + kTestImages[feature]; }

December 26, 2019 · 1 min · holdsky

快速排序C++实现

自己都忘了什么时候写的了,@_@ #pragma once #include <list> #include <vector> #include <stack> using namespace std; struct Bound { size_t low; size_t high; Bound(size_t lowind,size_t highind) { this->low=lowind; this->high=highind; } }; template <typename T> void QSort(vector<T> & vay) //从小到大排序 { size_t len=vay.size(); if( len > 2) { stack<Bound> boundS; Bound bd(0,len-1); boundS.push(bd); size_t low,high; while( ! boundS.empty() ) //当栈不为空时 { bd=boundS.top();//取栈顶元素 low=bd.low; high=bd.high; boundS.pop();//弹出栈顶元素 low++;//用数组的排序部分的首个元素作为关键值,进行比较 while( true ) //进行二分数组排序 { while( low < high && vay[low] <= vay[bd.low] ) //用数组的排序部分的首个元素作为关键值,进行比较 { low++; } while( low < high && vay[high] > vay[bd.low] ) //vay[high] > vay[bd.low] 不能用 >=,因为要二分数组的排序部分,保证两部分的排他性 { high--; } if( low < high ) {//交换低索引和高索引元素 T tmp=vay[low]; vay[low]=vay[high]; vay[high]=tmp; } else {//此时,low == high 为真 break; } } //此时,low == high 为真 if( vay[low] > vay[bd.low] ) { low--; } //将关键值插入大元素和小元素的分界 T tmp=vay[low]; vay[low]=vay[bd.low]; vay[bd.low]=tmp; if( low - bd.low > 1 ) //若小元素部分元素个数大于1个,继续排序 { Bound newbd(bd.low,low-1); boundS.push(newbd); } if( bd.high - low > 1)//若大元素部分元素个数大于1个,继续排序 { Bound newbd(low+1,bd.high); boundS.push(newbd); } } } else if( len == 2) //若数组长度为2,直接比较 { if( vay[0] > vay[1] ) { T tmp=vay[0]; vay[0]=vay[1]; vay[1]=tmp; } } //若数组长度小于2,不用排序 } template <typename T> struct BoundIter { BoundIter(typename list<T>::iterator lowiter,typename list<T>::iterator highiter) { this->low=lowiter; this->high=highiter; } typename list<T>::iterator low; typename list<T>::iterator high; }; template <typename T> void QSort(list<T> & lst) //从小到大排序 { if( lst.size() > 2) { stack<BoundIter<T>> boundIterS; BoundIter<T> bd(lst.begin(),(--lst.end())); boundIterS.push(bd); list<T>::iterator low,high; while( ! boundIterS.empty() ) //当栈不为空时 { bd=boundIterS.top();//取栈顶元素 low=bd.low; high=bd.high; boundIterS.pop();//弹出栈顶元素 low++;//用数组的排序部分的首个元素作为关键值,进行比较 while( true ) //进行二分数组排序 { while( low != high && *low <= *(bd.low) ) //用数组的排序部分的首个元素作为关键值,进行比较 { low++; } while( low != high && *high > *(bd.low) ) //*high > *(bd.low) 不能用 >=,因为要二分数组的排序部分,保证两部分的排他性 { high--; } if( low != high ) {//交换低索引和高索引元素 T tmp=*low; *low=*high; *high=tmp; } else {//此时,low == high 为真 break; } } //此时,low == high 为真 if( *low > *bd.low ) { low--; } //将关键值插入大元素和小元素的分界 T tmp=*low; *low=*bd.low; *bd.low=tmp; if( low != bd.low ) {//若小元素部分元素个数大于1个,继续排序 low--; if( low != bd.low ) { BoundIter<T> newbd(bd.low,low); boundIterS.push(newbd); } low++; } if( low != bd.high) {//若大元素部分元素个数大于1个,继续排序 low++; if( low != bd.high ) { BoundIter<T> newbd(low,bd.high); boundIterS.push(newbd); } } } } else if( lst.size() == 2) //若数组长度为2,直接比较 { list<T>::iterator iter0=lst.begin(); list<T>::iterator iter1=(++lst.begin()); if( *iter0 > *iter1 ) { T tmp=*iter0; *iter0=*iter1; *iter1=tmp; } } //若数组长度小于2,不用排序 }

December 25, 2019 · 2 min · holdsky

学习笔记:WindowManager显示Android全局悬浮窗口

我用Android手机装了个电商软件,抢购用。自己手机的状态栏不能显示秒级别的时间,只能精确到分钟。为了能准确的把握抢购时间,自己边学习边开发了一个时间显示悬浮窗。 WindowManager 参考文章 WindowManager可以在其他应用最上层,甚至手机桌面最上层显示窗口。 使用Context.getSystemService(Context.WINDOW_SERVICE)来获取WindowManager。 API 17推出了Presentation,它将自动获取display的Context和WindowManager,可以方便地在另一个display上显示窗口 使用WindowManager继承自基类的addView方法和removeView方法来显示和隐藏窗口 WindowManager实现悬浮窗需要声明权限,在manifest中添加如下权限: 在MIUI上需要在设置中打开本应用的”显示悬浮窗”开关,并且重启应用,否则悬浮窗只能显示在本应用界面内,不能显示在手机桌面上。 具体过程 创建工程 用Android Studio创建一个Android Basic Activity工程 添加权限 修改 AndroidManifest.xml,添加下面结构的数据 <?xml version="1.0" encoding="utf-8"?> <manifest > <!-- 显示顶层浮窗权限 --> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"> </uses-permission> </manifest> 修改代码 给MainActivity添加两个属性 public class MainActivity extends AppCompatActivity { TextView textView; //用于显示时间 Timer timer;//用于定时刷新时间 ... } Android Basic Activity工程创建后,自带一个按钮,给按钮添加点击事件的处理 final View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View view) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); // 类型 TYPE_SYSTEM_OVERLAY ,当悬浮窗口在其他App窗口上面时,不会隔断触摸事件,其他App能正常使用。 //若要隔断触摸事件,可以使用TYPE_SYSTEM_ALERT params.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY; // 设置flag int flags = WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; // | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 如果设置了WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,弹出的View收不到Back键的事件 // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口 // 设置 FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按 params.flags = flags; // 不设置这个弹出框的透明遮罩显示为黑色 params.format = PixelFormat.TRANSLUCENT; params.width = 300; params.height = 50; params.gravity = Gravity.TOP; TextView top=new TextView(view.getContext()); //控件字体位置位于左边 top.setGravity(Gravity.LEFT); top.setText("悬浮窗口"); WindowManager windowManager = (WindowManager)view.getContext().getApplicationContext().getSystemService(Context.WINDOW_SERVICE); windowManager.addView(top,params); textView = top; startTimer();//启动定时器(实现在后面) } }; FloatingActionButton fab = findViewById(R.id.fab); //添加点击事件处理 fab.setOnClickListener(listener); 定时器刷新时间 ...

December 18, 2019 · 3 min · holdsky

编程之路总结:加密与解密--RSA要点

RSA加密算法是一种非对称加密算法. 对极大整数做因数分解的难度决定了RSA算法的可靠性。换言之,对一极大整数做因数分解愈困难,RSA算法愈可靠。假如有人找到一种快速因数分解的算法的话,那么用RSA加密的信息的可靠性就肯定会极度下降。 密钥 RSA为非对称加密算法,密钥分为私钥和公钥。 其中,公钥可以由私钥导出(意思就是,拥有私钥,也就拥有了公钥),而公钥不能导出私钥。 密钥存储格式 比较常用的格式有ASN1.DER 和 PEM ASN1.DER 是公钥或者私钥的采用ASN1.DER方式序列化,其结果是一串二进制数据 PEM 为Base64格式的文本,主要内容为ASN1.DER数据的Base64表达。另外,PEM允许使用指定的对称加密算法将加密,如DES等。 PEM格式的样例,中间的Base64文本其实就是ASN1.DER的Base64表示 -----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDA04KNnZEBO3Ajk+nI4x/VKdoL F+yNv/KI0wCNF1J9do8Ra+MXUdDr+Nbehf6JYLuwHfur2mhw0gTT9lTLM2mD0qHo rbMs/XtSbXc9eP3xwJ/hU0V76LqtKw+y7GSgJ9XiyWS848DO2fV7uHdpwgRWCfHu R8fcUO0SrOHLj5g1HwIDAQAB -----END PUBLIC KEY----- 密钥长度 密钥长度越长,安全性越好,加密解密所需时间越长。 RSA长度有512、1024 、 2048 、 4096,一般使用时的最低长度为1024。 参考 文章,密钥长度增长一倍,公钥操作所需时间增加约 4 倍,私钥操作所需时间增加约 8 倍,公私钥生成时间约增长 16 倍。 加密与解密 RSA的加密密钥可以是公钥、也可以是私钥;解密亦然。 公钥加密,私钥解密 私钥加密,公钥解密 块加密与补全模式 RSA也是一种分块加密算法,单次加密长度的上限时密钥长度,而当RSA使用PKCS1 补全模式时, padding 占用了 11 个字节,因此单次计算块长度约束 待加密的字节数不能超过密钥的比特长度值除以 8 再减去 11 例如,密钥长度是1024比特,那么单次块最大长度 (1024 / 8 -11) = 117 字节 签名与验签 对于非对称加密算法来说,除了常规的加解密外,还可以用来签名与验签。 签名用私钥 验签用公钥 另外,就RSA来讲,签名的摘要算法可以使用 MD5、 SHA1、 SHA2系列(如SHA256)

December 17, 2019 · 1 min · holdsky

Base64:一个仅基于C++的实现

#pragma once //从ATL的base64库整理而来 //整理的目的是提供一个仅基于标准c++的base64库,提高代码的可移植性 #include <string> namespace Crypt { class Base64 { public: enum Base64_Flag { ATL_BASE64_FLAG_NONE = 0, // 0000 0000 标准的编码方式(RFC2045) ATL_BASE64_FLAG_NOPAD = 1 , // 0000 0001 没有补全( = ) ATL_BASE64_FLAG_NOCRLF = 2 // 0000 0010 没有换行 ( crlf ) } ; //计算编码后的长度 static int EncodeGetRequiredLength(int nSrcLen, Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { int nRet = static_cast<int>( (nSrcLen<<2) /3); if ((dwFlags & ATL_BASE64_FLAG_NOPAD) == 0)// 需要补全 = nRet += nSrcLen % 3; int nOnLastLine = nRet % 76; if (nOnLastLine) { if ( (nOnLastLine & 0x03) ) //nOnLastLine % 4 nRet += 4-(nOnLastLine & 0x03 ); } if ((dwFlags & ATL_BASE64_FLAG_NOCRLF) == 0) //需要换行符 { nRet += ((nRet / 76 + 1) << 1) ;//(nRet / 76 + 1) * 2, 回车换行个数(+1是为了取最多值),根据rfc2045,编码后一行最多76个字符(不含换行) } return nRet; } inline static int DecodeGetRequiredLength(int nSrcLen) { return ((nSrcLen*3)>>2)+2; //(nSrcLen*3)/4+2 } //pbSrcData 要编码的原始数据 //nSrcLen 原始数据长度 //szDest 编码后的输出 //pnEncodeRealLen 编码后的实际长度 static bool Encode(const unsigned char *pbSrcData,int nSrcLen,char* szDest,int *pnEncodeRealLen,Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { static const char *s_chBase64EncodingTable="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; if (!pbSrcData || !szDest || !pnEncodeRealLen) { return false; } int nWritten = 0 ; //编码后的长度,EncodeGetRequiredLength获得的长度通常大于实际长度 int nLen1 = (nSrcLen/3) << 2; // (nSrcLen/3)*4 int nLen2 = nLen1/76 ; //RFC2045 base64编码后,每行最多76个字符 int nLen3 = 19 ; for (int i=0; i<=nLen2; i++) { if (i==nLen2) nLen3 = (nLen1%76)/4; for (int j=0; j<nLen3; j++) { unsigned long dwCurr = 0; for (int n=0; n<3; n++) { dwCurr |= *pbSrcData++; dwCurr <<= 8; } for (int k=0; k<4; k++) { unsigned char b = (unsigned char)(dwCurr>>26); *szDest++ = s_chBase64EncodingTable[b]; dwCurr <<= 6; } } nWritten+= nLen3*4; if ((dwFlags & ATL_BASE64_FLAG_NOCRLF)==0) { *szDest++ = '\r'; *szDest++ = '\n'; nWritten+= 2; } } if (nWritten && (dwFlags & ATL_BASE64_FLAG_NOCRLF)==0) { szDest-= 2; nWritten -= 2; } nLen2 = (nSrcLen%3) ? (nSrcLen%3 + 1) : 0; if (nLen2) { unsigned long dwCurr = 0; for (int n=0; n<3; n++) { if (n<(nSrcLen%3)) dwCurr |= *pbSrcData++; dwCurr <<= 8; } for (int k=0; k<nLen2; k++) { unsigned char b = (unsigned char)(dwCurr>>26); *szDest++ = s_chBase64EncodingTable[b]; dwCurr <<= 6; } nWritten+= nLen2; if ((dwFlags & ATL_BASE64_FLAG_NOPAD)==0) { nLen3 = nLen2 ? 4-nLen2 : 0; for (int j=0; j<nLen3; j++) { *szDest++ = '='; } nWritten+= nLen3; } } *pnEncodeRealLen = nWritten; return true; } //szSrc 要解码的数据 //nSrcLen 要解码数据的长度 //pbDest 解码后的输出 //pnDecodeRealLen 解码后的字节长度 static bool Decode(const char* szSrc, int nSrcLen, unsigned char *pbDest, int *pnDecodeRealLen) { // walk the source buffer // each four character sequence is converted to 3 bytes // CRLFs and =, and any characters not in the encoding table // are skiped // returns -1 if the character is invalid // or should be skipped // otherwise, returns the 6-bit code for the character // from the encoding table static const int base64_decode_table[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; if (szSrc == 0 || pnDecodeRealLen == 0) { return false; } const char* szSrcEnd = szSrc + nSrcLen; int nWritten = 0; while (szSrc < szSrcEnd &&(*szSrc) != 0) { unsigned long dwCurr = 0; int i; int nBits = 0; for (i=0; i<4; i++) { if (szSrc >= szSrcEnd) break; int nCh = base64_decode_table[(unsigned char)(*szSrc)]; //强转为unsigned char,防止运行时抛出异常 szSrc++; if (nCh == -1) { // skip this char i--; continue; } dwCurr <<= 6; dwCurr |= nCh; nBits += 6; } // dwCurr has the 3 bytes to write to the output buffer // left to right dwCurr <<= 24-nBits; for (i=0; i<nBits/8; i++) { *pbDest = (unsigned char) ((dwCurr & 0x00ff0000) >> 16); pbDest++; dwCurr <<= 8; nWritten++; } } *pnDecodeRealLen = nWritten; return true; } static bool Encode(const std::string & in , std::string & out,Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { int len = EncodeGetRequiredLength(static_cast<int>(in.size()),dwFlags); out.assign(len,0); bool ret=Encode((const unsigned char*)in.c_str(),static_cast<int>(in.size()),(char*)out.c_str(),&len,dwFlags); out.resize(static_cast<size_t>(len)); return ret; } static bool Encode( const unsigned char * in ,int in_len,std::string & out ,Base64_Flag dwFlags = ATL_BASE64_FLAG_NONE) { int len = EncodeGetRequiredLength(in_len,dwFlags); out.assign(len,0); bool ret=Encode(in ,in_len,(char*)out.c_str(),&len,dwFlags); out.resize(static_cast<size_t>(len)); return ret; } static bool Decode(const std::string & in, std::string & out ) { out.assign(in.size(),0); int len=static_cast<int>(out.size()); bool ret = Decode(in.c_str(),static_cast<int>(in.size()), (unsigned char*) out.c_str() , &len); out.resize(static_cast<size_t>(len)); return ret; } static bool Decode(const char * in ,int in_len , std::string & out ) { out.assign( static_cast<size_t>(in_len),0); int outlen=static_cast<int>(out.size()); bool ret = Decode(in ,in_len, (unsigned char*) out.c_str() , &outlen); out.resize(static_cast<size_t>(outlen)); return ret; } }; }

December 13, 2019 · 5 min · holdsky

Objective-C在升序数组中使用二分法查找元素

//在升序数组中,查找插入索引 ,插入索引是第一个恰当的位置(多个相同元素的第一个元素位置) //existSame ,输出YES时,返回的索引是相等的元素索引,输出NO,返回的索引是需要插入的位置索引 NSUInteger FindInsertIndex(NSArray * ascSortedItmes , id obj, BOOL &existSame, int(^CompareTwoItem)(id item, id obj)) { existSame = NO; NSUInteger count = [ascSortedItmes count]; if (count > 0) { NSUInteger midIndex ,startIndex = 0 , endIndex = count -1 , safeN = count; while (safeN--) //safeN 保证while一定会结束 ,可以去掉这个判断 { midIndex = ((startIndex + endIndex) >> 1); // (startIndex + endIndex) / 2 id item = ascSortedItmes[midIndex]; int cmp = CompareTwoItem(item ,obj); if (cmp >0 ) // item > obj { if (endIndex == startIndex // 此时 endIndex == midIndex == startIndex || midIndex == 0 ) { return midIndex; } endIndex = midIndex -1; } else if (cmp == 0) //处理相等情况,确保插入的索引是第一个恰当的索引(多个相同元素的第一个元素位置) { if (midIndex == startIndex) //此时,startIndex到endIndex最多两个元素 { existSame = YES; return midIndex; } endIndex = midIndex; } else // item < obj { startIndex = midIndex + 1; if (startIndex > endIndex) // 此时 midIndex == endIndex { return (startIndex < count) ? startIndex : count; } } } ASSERT(0);//上面查找算法有问题 return count;//表示附加到末尾 } else { return 0; } }

December 13, 2019 · 1 min · holdsky

编程之路总结:加密与解密

先摘录一段百度百科的文字: 密码学(在西欧语文中,源于希腊语kryptós“隐藏的”,和gráphein“书写”)是研究如何隐密地传递信息的学科. 密码学的历史很长,人类使用密码的历史几乎与使用文字的时间一样长。古典密码学、近代密码学、现代密码学……详细请阅读 这里 在现代,密码技术大部分都和计算机紧密相关,因为计算了实在是太大了。做过基础应用开发的话,一般会对密码工具或加密工具有所了解,接触一些 CryptoXXX 或 XXXCrypto 工具库。Crypto是不是和希腊语kryptós很像?😄😄😄 好了,说些我懂的吧。我从软件编程的角度谈下自己的体会。 算法分类 在学习和使用Crypto类的加解密工具库时,尤其是在查看和使用其提供API时,要明白这后面对应的是什么算法、在什么场景下使用。 当前,我们常用的算法可以分为三类: 摘要算法,也称散列、哈希算法,也有叫做单向加密算法的。严格来说,这不是加密算法—-摘要算法是一种操作不可逆的算法,不能反向解密出原始数据。它应用的场景往往是完整性、真实性校验,例如数字签名。 对称加密算法。指加密和解密的密钥都是同一个,通常是加密方和解密方预先共同约定一个密钥。在非对称加密算法 非对称加密算法,公钥加密算法。这和对称加密算法相对应,指加密密钥和解密密钥不是同一个。著名的应用场景就是TLS/SSL(HTTPS的 S 指的是TLS/SSL) 摘要(Digest) 也叫杂凑、哈希;就是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,而不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。例如MD5是将数据散列到128比特(16字节)上。 MD5 英文 MD5 Message-Digest Algorithm. 是第5代摘要算法,前面还有MD2、MD4。 MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。–摘自百度百科。 算法输入单次计算块输出MD2任意数据,包括空数据512比特(64字节)128比特(16字节)MD4任意数据,包括空数据512比特(64字节)128比特(16字节)MD5任意数据,包括空数据512比特(64字节)128比特(16字节) MD5的计算结果不存在32位、16位之分,其结果就是16字节、128比特。32位、16位之说是极其不专业的。 MD5已经被证明不安全,但这并不影响我们继续使用,只需要我们将应用范围缩小到不涉及安全方面就可以了。 例如, 我们仍然可以使用MD5来校验非核心文件的完整性。 用MD5将URL映射到本地文件名,以帮助实现缓存功能。(通常在客户端使用MD5时,是不考虑MD5的碰撞概率的)。 SHA系列 英语 Secure Hash Algorithm,缩写为SHA 算法输入单次计算块输出SHA10 ~ (2^64 - 1)字节,包括空数据512比特(64字节)160比特(20字节)SHA2240 ~ (2^64 - 1)字节,包括空数据512比特(64字节)224比特(28字节)SHA2560 ~ (2^64 - 1)字节,包括空数据512比特(64字节)256比特(32字节)SHA3840 ~ (2^128 - 1)字节,包括空数据1024比特(128字节)284比特(48字节)SHA5120 ~ (2^128 - 1)字节,包括空数据1024比特(128字节)512比特(64字节) SHA224、SHA256、SHA384、SHA512统称为SHA2。当前,SHA1已经被证明不安全了(Google曾公布过两幅不同的有意义的图片,但其SHA1值是一样的,这在法律上是极其可怕的事)。 SHA2在安全领域仍然继续服役着。 SHA系列的最新标准已经有了SHA3系列,详情可以参考维基百科 SHA3 SM3 我国SM3密码算法正式成为ISO/IEC国际标准 SM3是中华人民共和国政府采用的一种密码散列函数标准,相关标准为“GM/T 0004-2012 《SM3密码杂凑算法》”。 在商用密码体系中,SM3主要用于数字签名及验证、消息认证码生成及验证、随机数生成等,其算法公开。据国家密码管理局表示,其安全性及效率与SHA256相当。 SM3 算法的哈希值大小为 256 比特。 ...

December 13, 2019 · 1 min · holdsky