1 | bcdedit /set hypervisorlaunchtype auto |
hyper-v.bat
,以管理员权限执行即可安装完整的 Hyper-V 组件:1 | pushd "%~dp0" |
Win10 中 WSL2 环境是一个 HyperV 管理的独立容器,其网络相对宿主系统独立,不像 WSL1 环境那样是和宿主系统网卡直接桥接起来的。WSL2 这样的架构虽然运行效率提高不少,但却无法像 WSL1 那样能直接使用宿主系统提供的代理服务,造成了不少麻烦。
解决办法很简单,只要允许代理服务为非 localhost
来源地址服务,然后直接使用宿主系统的 IP 地址就能访问到代理了。以 Ubuntu WSL2 环境访问 Shadowsocks 代理为例,步骤如下:
.bashrc
中加入如下的函数方便打开/关闭代理(假设宿主系统 IP 地址为 192.168.175.173
,Shadowsocks SOCKS5 代理在 1080
端口):1 | proxy () { |
这样在终端上就可直接用 proxy
开启代理,noproxy
关闭代理。
proxychains
来实现拦截其网络通信强制走代理的目的:1 | $ sudo apt install proxychains tor |
安装完毕后,向 /etc/proxychains.conf
增加如下配置:
1 | [ProxyList] |
然后就能用 proxychains <app_path> <cmdline>
的形式强制应用使用代理了。
在很多场景下(手头没有 U 盘、某台电脑 USB 口不可用或没有足够权限安装存储设备驱动等等),我们都需要通过网线直连的方式在两台电脑之间传输数据,这样就要求正确地将两台电脑设置为同一子网下的不同静态 IP。对于电脑知识较为丰富的用户来说此设置过程非常简单,但对于一般用户来说就比较困难了,此时就希望通过程序自动配置好这种网络环境。
这里主要针对 Windows XP 系统自动配置进行分析,因 Vista 以上版本系统已经有了 zeroconf
的实现,有现成的解决方案可用。
网卡地址要么是静态设置,要么是动态 DHCP 分配的。对于静态设置 IP 的情况,拔掉再插上网线后 Windows 系统会监测到媒介状态变化,并自动发送若干源 MAC 为对应网卡 MAC,源/目标 IP 均为当前网卡静态设置 IP 的 ARP 包(称为 Gratuitous ARP 包,用来向子网内其他机器或网关主张自己的静态 IP 所有权),若将网线另一侧机器的 IP 设置为同一子网内的地址,两台机器就能正常通讯了。
对于动态 DHCP 分配的情况,拔掉再插上网线后 Windows 系统会自动发送 DHCP 相关请求,要求可能存在的 DHCP 服务器分配 IP,若在网线另一侧机器上运行 DHCP 服务,即可自动设置双机 IP 在同一子网中,也能正常通讯。
参考实现程序在此。该程序使用 pcapy
模块操作 WinPCAP
在指定网卡(Windows 环境下通过 wmi
模块自动获得首个可用的有线网卡)上监听数据包,通过 impacket
模块解析后根据数据包特征决定使用静态还是动态配置逻辑,最后用 OS 相关方法设置本地监听的有线网卡 IP 后(Windows 环境下通过 wmi
模块实现)完成双机 IP 配置。
使用自己电脑的 N 卡进行 CUDA 加速时都希望能使用全部显存,但在 Ubuntu 18.04 LTS 中安装官方专有驱动后,如果用 nvidia-settings 将 PRIME profiles 设为 Nvidia,会让包括 X 桌面在内的所有图形加速功能都走 N 卡,会占用不少显存。若将 PRIME profiles 改为 Intel,N 卡驱动又不会被加载,无法使用 CUDA 加速。
PRIME profiles 是通过 /usr/bin/prime-select
命令完成切换的,PRIME profiles 设为 Intel 时该命令会生成一个驱动黑名单 /lib/modprobe.d/blacklist-nvidia.conf
,修改该文件内容将如下几行注释掉:
1 | blacklist nvidia |
logout 后重新 login 即可实现 X 桌面系统图形加速走集成显卡且 N 卡驱动正常加载的效果,可使用全部显存进行 CUDA 计算。
]]>__int128
内置类型,但系统自带的 gcc 4.4.x 并不支持该类型,所以需要按如下步骤装一个 gcc 4.9.x:1 | $ sudo yum install centos-release-scl |
String index out of range
异常,百思不得其解。经过一番研究,得到了这样一个最小复现用例:1 | import java.util.regex.*; |
跟踪后发现这是 Java 正则库中的一个实现 bug,用例模式中的 (.)*
会被 Pattern 类解析为 GroupCurly 算子,而该算子内对应贪婪匹配模式的 match0
方法实现有误,当某次匹配不成功需要进行回溯时,其假定 .
匹配的任意字符都是相同长度而每次都减去了最后一次匹配成功的字符长度;然而 Java 内部使用 UTF-16 编码保存字符串,仅有 Unicode BMP 内的字符(即码点小于等于 U+FFFF 的字符)才能用单个 char 表示,Unicode SMP 内的字符(即码点大于 U+FFFF 的字符)都需要用 2 个替代码元表示 1 个字符,当匹配目标字符串中有 BMP/SMP 区字符混合存在的情况且匹配不成功时,回溯逻辑的字符等长假定就会失效,导致字符串下标计算出错而出现访问越界异常。不过同在 Pattern 类中表达非捕获重复算子的 Curly 就没有这个问题,如果将以上用例中的 (.)*
改为 .*
就一切正常了。
经过一番搜索,发现该 bug 已经在 2013-02-01 被人提交给 JDK buglist (见这里) ,JDK 5~7 都受其影响,不过该 bug 已经在 JDK 8 中修复了。考虑到 Java 正则库实现细节非常不堪,也许还会有更多的坑在实践中被发现,定期关注一下 java.util.regex
相关的 bug (见这里) 应该是个好主意……
1 | ipython nbconvert --to latex example.ipynb |
\documentclass
行之后增加如下内容:\usepackage{fontspec, xunicode, xltxtra}\usepackage[slantfont, boldfont]{xeCJK} % 允许斜体和粗体\setCJKmainfont{WenQuanYi Micro Hei} % 默认中文字体\setCJKmonofont{WenQuanYi Micro Hei Mono} % 中文等宽字体\setmainfont{TeX Gyre Pagella} % 英文衬线字体\setmonofont{Monaco} % 英文等宽字体\setsansfont{Trebuchet MS} % 英文无衬线字体\punctstyle{kaiming} % 开明式标点格式: 句末点号用全角, 其他半角
\usepackage{babel}
这一行\date{...}
和 \author{...}
中的内容改为需要的日期和作者信息xelatex
命令直接将修改后的 LaTeX 文件转为 PDF 即可:1 | xelatex example.tex |
另外,最后转 PDF 时若出现类似这样的错误:
> Runaway argument?> ! File ended while scanning use of @writefile.>> par> l.110 begin{document}
可以直接删除 *.aux
文件再重新转换即可。
大家都知道 JVM 有 HotSpot 引擎可以对热代码路径进行有效的 JIT 优化,大幅度提升计算密集代码的性能,但 HotSpot 对自己编写的 Java 代码时进行了哪些 JIT 优化?优化后生成的 native code 长什么样?知道了这些信息之后,才能有的放矢地改进自己的代码。
以如下 Java 代码为例:
1 | // Accumulator.java |
一般情况下,我们只需要知道 JIT 内联函数是否满足自己的期望即可,可以用如下命令行达到这个目的:
1 | javac Accumulator.java |
注意默认情况下一个方法至少要被调用 10k 次以上才可能被 JIT 优化,故这里运行时给出的循环参数也要大于该值,输出结果如下:
61 1 Accumulator::addSqrt (7 bytes) 62 2 Accumulator::sqrt (6 bytes) 67 3 % Accumulator::addAllSqrts @ 4 (23 bytes)...
各个数据列的含义分别为:
列1 | 列2 | 列3 | 列4 | 列5 |
---|---|---|---|---|
自 JVM 启动到尝试优化的时间(ms) | 尝试优化的顺序(从 1 开始) | 特殊方法标识[1] | 方法全名及生成代码长度 | 若有 "made not entrant" 或 "made zombie" 字样就表明没有进行优化 |
用如下命令行可以更多地了解内联优化的实际情况以及优化发生的级别:
1 | java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+TieredCompilation Accumulator 50000 |
输出结果如下:
50 1 3 java.lang.String::equals (81 bytes) 51 2 3 java.lang.Object::(1 bytes) 53 3 n 0 java.lang.System::arraycopy (native) (static) 53 4 3 java.lang.String::charAt (29 bytes) @ 18 java/lang/StringIndexOutOfBoundsException:: (not loaded) not inlineable 54 6 3 java.lang.String::hashCode (55 bytes) 55 5 3 java.lang.String::indexOf (70 bytes) @ 66 java.lang.String::indexOfSupplementary (71 bytes) callee is too large 56 7 3 java.lang.String::length (6 bytes) 59 8 3 java.lang.Math::min (11 bytes) 61 9 3 Accumulator::addSqrt (7 bytes) @ 2 Accumulator::sqrt (6 bytes) @ 2 java.lang.Math::sqrt (5 bytes) intrinsic 62 10 3 Accumulator::sqrt (6 bytes) @ 2 java.lang.Math::sqrt (5 bytes) intrinsic 64 11 4 Accumulator::addSqrt (7 bytes)...
这里新增的第 3 列代表了优化发生的级别(Tier 0~4,其中 Tier 0 为 Interpreter,Tier 1~3 为 client 模式的优化策略,Tier 4 为 server 模式的优化策略)
为了更好地了解 JIT 优化效果,查看生成的 native code 是很有必要的,不过 OpenJDK 并没有自带反汇编插件 hsdis-*.so
,需要手工安装一下该插件。一般来说有这样几种安装方法:
sudo apt-get install libopenjdk-hsdis
安装该插件安装完成后,即可用一下命令输出所有 JIT 后函数的 native code 汇编代码:
1 | java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Accumulator 50000 |
输出结果如下:
Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional outputLoaded disassembler from hsdis-amd64.soDecoding compiled method 0x00007ffd9905ee50:Code:[Disassembling for mach='i386:x86-64'][Entry Point][Verified Entry Point][Constants] # {method} 'addSqrt' '(DI)D' in 'Accumulator' # parm0: xmm0:xmm0 = double # parm1: rsi = int # [sp+0x20] (sp of caller) 0x00007ffd9905ef80: sub $0x18,%rsp 0x00007ffd9905ef87: mov %rbp,0x10(%rsp) ;*synchronization entry ; - Accumulator::addSqrt@-1 (line 16) 0x00007ffd9905ef8c: vcvtsi2sd %esi,%xmm1,%xmm1 0x00007ffd9905ef90: vsqrtsd %xmm1,%xmm1,%xmm1 0x00007ffd9905ef94: vaddsd %xmm1,%xmm0,%xmm0 ;*dadd ; - Accumulator::addSqrt@5 (line 16) 0x00007ffd9905ef98: add $0x10,%rsp 0x00007ffd9905ef9c: pop %rbp 0x00007ffd9905ef9d: test %eax,0xc5f905d(%rip) # 0x00007ffda5658000 ; {poll_return} 0x00007ffd9905efa3: retq 0x00007ffd9905efa4: hlt 0x00007ffd9905efa5: hlt...
还可以增加选项 -XX:PrintAssemblyOptions=hsdis-print-bytes
额外输出指令实际字节序列:
1 | java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:PrintAssemblyOptions=hsdis-print-bytes Accumulator 50000 |
输出结果为:
Java HotSpot(TM) 64-Bit Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional outputLoaded disassembler from hsdis-amd64.soDecoding compiled method 0x00007f482105ee50:Code:[Disassembling for mach='i386:x86-64'][Entry Point][Verified Entry Point][Constants] # {method} 'sqrt' '(I)D' in 'Accumulator' # parm0: rsi = int # [sp+0x20] (sp of caller) 0x00007f482105ef80: sub $0x18,%rsp ;...4881ec18 000000 0x00007f482105ef87: mov %rbp,0x10(%rsp) ;...48896c24 10 ;*synchronization entry ; - Accumulator::sqrt@-1 (line 20) 0x00007f482105ef8c: vcvtsi2sd %esi,%xmm0,%xmm0 ;...c5fb2ac6 0x00007f482105ef90: vsqrtsd %xmm0,%xmm0,%xmm0 ;...c5fb51c0 ;*invokestatic sqrt ; - Accumulator::sqrt@2 (line 20) 0x00007f482105ef94: add $0x10,%rsp ;...4883c410 0x00007f482105ef98: pop %rbp ;...5d 0x00007f482105ef99: test %eax,0xc048061(%rip) # 0x00007f482d0a7000 ;...85056180 040c ; {poll_return} 0x00007f482105ef9f: retq ;...c3[Exception Handler][Stub Code] 0x00007f482105efa0: jmpq 0x00007f482105eda0 ;...e9fbfdff ff ; {no_reloc}[Deopt Handler Code] 0x00007f482105efa5: callq 0x00007f482105efaa ;...e8000000 00 0x00007f482105efaa: subq $0x5,(%rsp) ;...48832c24 05 0x00007f482105efaf: jmpq 0x00007f4821038f00 ;...e94c9ffd ff ; {runtime_call} 0x00007f482105efb4: hlt ;...f4...
上述命令总是输出所有方法的 JIT 结果,当结果很多时难以看出重点,可以将 -XX:+PrintAssembly
选项改为 -XX:CompileCommand=print,*class.method
来限制输出哪些方法,例如:
1 | java -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand='print,*Accumulator.sqrt' Accumulator 50000 |
这样输出结果就变得很少了:
CompilerOracle: print *Accumulator.sqrtJava HotSpot(TM) 64-Bit Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional outputCompiled method (c2) 62 2 Accumulator::sqrt (6 bytes) total in heap [0x00007fcacc1d18d0,0x00007fcacc1d1a98] = 456 relocation [0x00007fcacc1d19f0,0x00007fcacc1d19f8] = 8 main code [0x00007fcacc1d1a00,0x00007fcacc1d1a20] = 32 stub code [0x00007fcacc1d1a20,0x00007fcacc1d1a38] = 24 oops [0x00007fcacc1d1a38,0x00007fcacc1d1a40] = 8 scopes data [0x00007fcacc1d1a40,0x00007fcacc1d1a50] = 16 scopes pcs [0x00007fcacc1d1a50,0x00007fcacc1d1a90] = 64 dependencies [0x00007fcacc1d1a90,0x00007fcacc1d1a98] = 8Loaded disassembler from hsdis-amd64.soDecoding compiled method 0x00007fcacc1d18d0:Code:[Disassembling for mach='i386:x86-64'][Entry Point][Verified Entry Point][Constants] # {method} 'sqrt' '(I)D' in 'Accumulator' # parm0: rsi = int # [sp+0x20] (sp of caller) 0x00007fcacc1d1a00: sub $0x18,%rsp 0x00007fcacc1d1a07: mov %rbp,0x10(%rsp) ;*synchronization entry ; - Accumulator::sqrt@-1 (line 20) 0x00007fcacc1d1a0c: vcvtsi2sd %esi,%xmm0,%xmm0 0x00007fcacc1d1a10: vsqrtsd %xmm0,%xmm0,%xmm0 ;*invokestatic sqrt ; - Accumulator::sqrt@2 (line 20) 0x00007fcacc1d1a14: add $0x10,%rsp 0x00007fcacc1d1a18: pop %rbp 0x00007fcacc1d1a19: test %eax,0x9ea55e1(%rip) # 0x00007fcad6077000 ; {poll_return} 0x00007fcacc1d1a1f: retq[Exception Handler][Stub Code] 0x00007fcacc1d1a20: jmpq 0x00007fcacc1ceda0 ; {no_reloc}[Deopt Handler Code] 0x00007fcacc1d1a25: callq 0x00007fcacc1d1a2a 0x00007fcacc1d1a2a: subq $0x5,(%rsp) 0x00007fcacc1d1a2f: jmpq 0x00007fcacc1a8f00 ; {runtime_call} 0x00007fcacc1d1a34: hlt 0x00007fcacc1d1a35: hlt 0x00007fcacc1d1a36: hlt 0x00007fcacc1d1a37: hltOopMapSet contains 0 OopMaps
Tips: 用如下命令能看到 JVM 支持的所有内部选项及其当前值,可以用于排查环境问题或进行细节调整:
1 | java -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version |
!
表示是异常处理函数, n
表示是 native 函数, %
表示进行了 OSR(On-Stack Replace) 优化 ↩︎
用 USB 线连接手机,执行 lsusb
命令后可以看到如下内容:
1 | Bus 001 Device 010: ID 4bb0:30d2 |
那么 vendor id 就是 0x4bb0
编辑 /etc/udev/rules.d/51-android.rules
,加入如下内容:
1 | SUBSYSTEM=="usb", ATTRS{idVendor}=="4bb0", MODE="0666", GROUP="plugdev" |
这里 ATTRS{idVendor}
条件之后填的就是上面获得的 vendor id。
添加完毕后执行 sudo reload udev
重新加载新的 udev 规则,然后重新插拔一次手机
做完以上步骤后 adb devices
仍然不能识别手机,因为京崎的 vendor id 并没有放在 Android SDK 的默认厂商列表中。
这里需要修改 ~/.android/adb_usb.ini
,在最末尾增加 16 进制格式的 vendor id,这里增加后的内容就是:
1 | # ANDROID 3RD PARTY USB VENDOR ID LIST -- DO NOT EDIT. |
注意运行 android update adb
会清空该文件,慎用此命令!
最后执行如下命令重启 adb server 即可:
1 | adb kill-server |
看到 adb devices
显示如下字样就是成功了:
1 | List of devices attached |
chroot <new-root-dir>
不足以正常运行一些程序,因为很多程序需要访问 procfs
、 sysfs
、 devfs
等特殊的子目录,直接 chroot
更改根目录后这些子目录没有被挂载,内容都是空,导致这些程序出现问题。完整的 chroot
前准备过程可以通过下列命令完成:
1 | cd <new-root-dir> |
上述命令将宿主系统的 procfs
、 sysfs
、 devfs
等特殊目录挂载到新的根目录下对应子目录上,并设置 chroot
系统使用宿主系统的域名解析配置,最后用以下命令即可完成 chroot
操作:
1 | chroot <new-root-dir> /bin/bash |
参考:
]]>一直以来却不甚知道为什么用这些词来表达,其含义究竟是什么?最近研究了一番才了解到了一些端倪。大体上是因为现代科学基本上是从古希腊到古罗马一系传承下来的,其用语自然受到希腊语和拉丁语的影响,且由于各个学科门类的出现时间和跨度相差很大,往往会出现多种语源的词汇混合在一起使用的情形。
古希腊人爱好几何学研究,可以猜到几何形状的描述肯定是希腊词根了。而其他很多场景下用的则是拉丁词根或希腊/拉丁组合词根。下面是摘抄自 wikipedia 的一些数字词根:
数值 | 表基数 | 表倍数 |
---|---|---|
1/2 | hemi- | - |
1 | hen- | mono-, haplo- |
2 | di-, dy-, duo- | dis- |
3 | tri- | tris-, |
4 | tetra- | tetrakis- |
5 | penta- | pentakis- |
6 | hexa- | hexakis- |
7 | hepta- | heptakis- |
8 | ogdo-, octa-, octo- | octakis- |
9 | ennea- | enneakis- |
10 | deca- | decakis- |
16 | hexakaideca-, hexadeca- | hexadecakis- |
数值 | 表基数 | 表倍数 | 表分配(即每多少个的意思) | 表序数(即第多少个的意思) |
---|---|---|---|---|
1/2 | semi- | - | demi- | - |
1 | uni- | sim- | singul- | prim- |
2 | du- | bi-, bis- | bin- | second- |
3 | tri- | ter- | tern-, trin- | terti- |
4 | quadri-, quadru- | quater- | quatern- | quart- |
5 | quinque- | - | quin- | quint- |
6 | sexa- | - | sen- | sext- |
7 | septem-, septi- | septem-, septi- | septen- | septim- |
8 | octo- | - | octon- | octav- |
9 | novem- | novem- | noven- | nona- |
10 | decem-, dec- | decem-, dec- | den- | decim- |
16 | sedec-, sexdec- | sedec-, sexdec- | sedec-, sexdec- | sedec-, sexdec- |
从拉丁语词根里是不是看到了一些熟悉的东西?呵呵,电影里经常看到的半神 demigod 的 demi- 就是一半的意思,而英语里表达月份的名词实际上就是罗马人用拉丁语数第几个月了,不过个别月份为了纪念神明、节期或皇帝而变更了名称,且原始的罗马历法中一年只有 10 个月,现在的 3 月是当时的第一个月,12 月则是第十个月,漫长的冬日则认为无需计算月份,后来变更历法时才加入现在的 1 月和 2 月。各个月份名的含义如下:
月份名称 | 含义 |
---|---|
January | 取自罗马神话中门神 Janus,表达一年开始的意思 |
February | 取自拉丁语 februum (即净化),因为这段时间内会举行传统的净化仪式 |
March | 取自罗马神话中战神 Mars |
April | 可能源自拉丁语 aperire (即开放),暗示这个季节树木花草都开始发芽,也可能取自希腊神话中美神 Aphrodite |
May | 取自希腊神话中女神 Maia |
June | 取自罗马神话中女神 Juno |
July | 本来叫 Quintillis (即第 5 个月,词根 quin-),后为纪念皇帝 Julius Caesar 改名 |
August | 本来叫 Sextillis (即第 6 个月,词根 sexa-),后为纪念皇帝 Augustus 改名 |
September | 即第 7 个月 (词根 septem-) |
October | 即第 8 个月 (词根 octo-) |
November | 即第 9 个月 (词根 novem-) |
December | 即第 10 个月 (词根 decem-) |
而计算机中常见的 NULL 或 NIL 则来自拉丁语 nulla 和 nil (表示什么都没有的意思),不过 nil 在希腊语中也表达类似的意思。表达 16 进制的单词 hexadecimal 比较奇葩,是一个希腊/拉丁混合词,以前有人争论说应该改成纯拉丁词 senidenary 或 sedenary,后来也没能挡住习惯的力量。
参考:
]]>打 RPM 包比较简单,现在的模块一般都是 setuptools 规范的,只要简单地进入模块源码目录,运行 python setup.py bdist_rpm
即可打出 RPM 包。
打 DEB 包需要 stdeb 工具的支持,安装该工具后可以用如下命令打出 DEB 包:
pip install -d . uncertainties
uncertainties-2.4.1.tar.gz
,执行 py2dsc uncertainties-2.4.1.tar.gz
将其转换为 Debian 源码包py2dsc
生成的打包目录打包:cd deb_dist/uncertainties-2.4.1/
dpkg-buildpackage -rfakeroot -uc -us
如果只是想直接安装转换后的 DEB 包,则可以简单地用命令 sudo pypi-install uncertainties
完成,无需使用上面较麻烦的步骤。
参考:
]]>1 | <script type="text/x-mathjax-config"> |
这里的 displayIndent
可以控制左对齐公式的缩进宽度,若设为 "0" 就表示同正文平齐。下面是具体的例子:
P.S. 虽然 MathJax 支持直接识别很多 LaTeX 的环境块,但通常 Wordpress 上的 LaTeX 插件仅在检测到自己识别的 LaTeX 标签时才会生成加载 MathJax 的代码,如果 blog 中只使用了 LaTeX 环境块就会导致无法加载 MathJax 而全部显示为普通文本。解决方法是在正文某处显式写一个空内容的可识别 LaTeX 标签欺骗 LaTeX 插件加载 MathJax。
参考:https://github.com/mathjax/mathjax-docs/wiki/MathML-alignment-Left-or-Right
]]>(setq org2blog/wp-use-sourcecode-shortcode t)
最后,org2blog 依赖于 org-mode 的 HTML 导出功能,后者总是会转义所有 XML/HTML 特殊字符,但当前版本的 SyntaxHighlighter Evolved 插件也总是会转义这些符号,造成双重转义使高亮输出发生混乱。解决方式是修改 SyntaxHighlighter Evolved 插件,将 syntaxhighlighter.php
中的 encode_shortcode_contents_callback()
函数改为如下形式:
1 | function encode_shortcode_contents_callback( $atts, $code = '', $tag = false ) { |
这样可以在源码区块内容已经转义的情况下不再进行二次转义,从而解决输出混乱问题。
下面是一个语法高亮的例子:
1 |
|
error: code too large
。但问题是在 Eclipse 中编译使用该文件完全没有问题,由于 65535 bytes 的限制是 Java VM 规范所规定,同生成 class 的编译器没有关系,因此原因只可能是 ecj 生成的 bytecode 比 javac 少,这就有点儿诡异了。要查看 class 中的字节码可以用 javap
程序完成,通过下面的命令可以将有问题的 class (这里假设是 Test)的静态初始化匿名 method 中所有 bytecode 输出:
1 | javap -c Test | sed -ne '/static {}/,/return/ p' |
将 Test class 的 static field 用对分法逐渐裁剪到 javac 刚好能编译过去,然后用上述命令导出 javac 编译结果和 ecj 编译结果进行对比分析,果然发现了一些差异(测试代码可从此处下载)。
例如如下的这段 Java 代码:
1 | public static final long[][] FOLLOW_DEFAULT_in_table_option26937 = |
由 ecj 编译产出的 bytecode 为:
offset | bytecode | stack [before]->[after] |
---|---|---|
0 | iconst_1 | -> 1 |
1 | anewarray #idx | count -> arrayref |
4 | dup | value -> value, value |
5 | iconst_0 | -> 0 |
6 | iconst_2 | -> 2 |
7 | newarray long | count -> arrayref |
9 | dup | value -> value, value |
10 | iconst_1 | -> 1 |
11 | ldc2_w #idx | -> value |
14 | lastore | arrayref, index, value -> |
15 | aastore | arrayref, index, value -> |
16 | pustatic #idx | value -> |
由 javac 编译产出的 bytecode 为:
offset | bytecode | stack [before]->[after] |
---|---|---|
0 | iconst_1 | -> 1 |
1 | anewarray #idx | count -> arrayref |
4 | dup | value -> value, value |
5 | iconst_0 | -> 0 |
6 | iconst_2 | -> 2 |
7 | newarray long | count -> arrayref |
9 | dup | value -> value, value |
10 | iconst_0 | -> 0 |
11 | iconst_0 | -> 0 |
12 | lastore | arrayref, index, value -> |
13 | dup | value -> value, value |
14 | iconst_1 | -> 1 |
15 | ldc2_w #idx | -> value |
18 | lastore | arrayref, index, value -> |
19 | aastore | arrayref, index, value -> |
20 | pustatic #idx | value -> |
可以看到 javac 多生成了 offset 10~13 的 bytecode,分析可知这段代码的功能是将内部 long[] 数组第一个元素置为 0。也就是说,ecj 利用了 Java VM 规范中的默认初始化约定,不会为显式初始化为 0 的元素生成赋值 bytecode,但 javac 却会傻傻地按照初始化列表逐个生成 bytecode。这样当非 0 元素初始化很少时,ecj 就比 javac 生成的静态初始化 bytecode 少得多了,自然会发生 javac 编译报 code too large 错误但 ecj 正常的情况。
不过从另一方面看,ANTLR 3.x 生成的 parser 代码也确实没有考虑 Java VM 的 bytecode 长度限制,一旦语法复杂到一定程度,生成的代码很可能连 ecj 都无法编译通过。这是一个比较严重的设计疏忽,希望 ANTLR 后续版本使用变通方法对这一点进行改进。
为了在非 javac 编译器上也能享受 Lombok 带来的好处,一个可能的方案是实际编译之前先借助 javac 的内部运行时库对 Java 代码进行 delombok 操作,即将被 Lombok 修改后的 Java 代码导出成实际文件,再使用 Eclipse Compiler 这样的替代编译器对导出代码进行真正的编译操作。方法如下:
pom.xml
增加如下内容:1 | <build> |
pom.xml
以启用它:1 | <build> |
经过以上配置之后,Maven 项目的实际和测试 Java 代码中即可正常使用 Lombok annotation,也可以在 mvn compile
之后进入 target/generated-sources/delombok/
和 target/generated-test-sources/delombok/
目录自行查看被 Lombok 修改后的实际和测试代码。
git://linuxtv.org/media_build.git
检出代码编译安装驱动模块/etc/udev/rules.d/
中增加了其给出的 udev 规则以对设备结点权限进行修正,否则只有 root 才能访问 DVB 设备--enable-driver-detach
选项配置后编译安装,这样 rtl-sdr 工具一旦发现设备被该模块占用便会将其自动卸载。gnc_dbi_mysql_session_begin()
函数中主动执行 SET NAMES 'utf8'
语句设定 MySQL 连接字符编码为 UTF-8,但遗憾的是用来在连接故障时修复连接的 gnc_dbi_verify_conn()
函数并没有进行类似的操作。一旦数据库连接因网络问题意外断开,用户输入新交易时就会使用 gnc_dbi_verify_conn()
重新创建的新连接,而该连接的字符编码是默认的 latin1,直接后果就是输入中文等字符都变成了一堆乱码。实际上 GNUCash 底层使用的 libdbi 库是考虑了连接字符编码问题的,其专门提供了一个 "encoding" 的设置选项用来对连接字符编码进行通用设置,底层的各个 driver 在创建连接时都会尊重该设置。所以只要简单地增加该选项,不管连接怎么断开重连,字符编码都能保持正常。
打上附件中的 fix_charset.patch 就可以修正此问题了。
另外,General Ledger 功能实际上非常好用,有了它就不需要在各个 Account 之间来回切换查帐了。但 GNUCash 目前默认在 General Ledger 中只显示最近一个月的交易,可苦了想对更老交易对账的用户。
我对其进行了小小的修改,打上附件中的 enh_general_ledger.patch 即可让 General Ledger 显示当前年度的所有交易,这样对账就非常方便了。
更新: 最近发现 General Ledger 实际上可以自定义显示日期范围,只要在进入 General Ledger 界面后选择菜单上的 View/Filter...,即可在弹出对话框的 Date 页选择显示起止日期,无需使用附件中的补丁。
]]>1 | UPDATE splits SET memo = CONVERT(BINARY(CONVERT(memo USING latin1)) USING utf8) WHERE CHAR_LENGTH(CONVERT(BINARY(CONVERT(memo USING latin1)) USING utf8)) != CHAR_LENGTH(memo); |