今天看见了别人总结的,感觉太有用了。
num&0x1, 为1说明是奇数,为0说明是偶数。因为只看二进制位的最后一位
a % b,当底b为2的n次幂的时候,可以改写成 a & (b - 1)
原理也是按位只看最后n位的数据,高位直接不用看。
我这个还专门看了一下有多块,数量级差的太多了, 但初步能看见的时间来说,1亿次差了0.1秒,从时间成本上来说其实差别不大。
看了一下,差了四条汇编指令,还得考虑取数据的时间
Read more...
Archive for 算法-编程
64位Linux系统汇编中32位和64位程序的系统调用区别简单分析
写了个汇编程序,照着别人的写的,其中有个系统调用,然后就去搜了一下linux系统的系统调用表。这一搜不要紧,搜出问题来了。
我找到了两个版本,一个x86-64版本的,一个i386版本的。比如32位的sys_exit系统调用里调用号%eax为1,64位里sys_exit调用号%rax为60,这个不重要。然后我看了一下我的代码,我用了32位的调用号,功能也是32位的。第一反应就是编译的elf是32位的,然后我看了一下readelf -h 64,为64位。后来发现还有个简单命令 直接file 64就能看。然后我就觉得很不可思议。
接着我查了两个版本的头文件在哪里,看怎么用的。学到很多东西。比如查看调用头文件
printf SYS_exit | gcc -include sys/syscall.h -E -
echo '#include ' | gcc -E -x c -l
echo '#include ' | gcc -E -x c - | egrep '# [0-9]+ ' | awk '{print $3;}' | sort -u
发现是找的是64位的,然后我更糊涂了。主要就这几个文件
# 1 "/usr/include/x86_64-linux-gnu/asm/unistd_64.h" 1 3 4
# 14 "/usr/include/x86_64-linux-gnu/asm/unistd.h" 2 3 4
# 25 "/usr/include/x86_64-linux-gnu/sys/syscall.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/syscall.h" 1 3 4
# 1 "/usr/include/x86_64-linux-gnu/asm/unistd_32.h"
看头文件确实是看是否定义i386,但是我其实是没用头文件的,直接写的中断号,用as编译的代码,没有使用gcc。
然后我查unistd_32.h和unistd_64.h的区别,然后找到原因了。
首先我64位系统,默认都是编译的64位二进制这个没啥问题。汇编和链接的时候可以制定生成32位:
as e.s --32 -o 32.o
ld 32.o -o 32
我生成32位汇编也是一样能执行的。
其次,32位汇编和64位汇编所使用的系统中断是不一样的。intel和amd都提供了硬件中断,而且还不兼容。为了兼容尽量使用的一下两个:
32位: sysenter sysexit.
64位: syscall sysret.
与这个相对应的是软件中断,那个速度比较慢,也是比较老的遗留下来的。int $0x80。
然后我代码用的是这个老的软中断。软中断用的都是32位的调用号,而且在64位的系统里,同样是可以使用的,因为他只使用低32位的寄存器。所以问题就解决了,我是用的软中断导致的,应该看软中断的调用号,而不是看编译的二进制是64位还是32位。
Read more...
Python源码特殊点分析3-运行导入相关
主要看pymain_run_filename,是文件执行pymain_open_filename,不是文件赋值fp = stdin,最后执行pymain_run_file(fp, pymain->filename, cf)。看这里应该交互和文件执行都在这一个里边的。
pymain_open_filename就是打开文件返回句柄。
pymain_run_file文件里,主要就是调用PyRun_AnyFileExFlags。下面应该与main.c没关系了,主要在Python/pythonrun.c里了.
pymain_repl里调用 PyRun_AnyFileFlags,然后也是调用的PyRun_AnyFileExFlags。
PyRun_AnyFileExFlags里, Py_FdIsInteractive判断是否是交互模式,如果是调用PyRun_InteractiveLoopFlags,里边有个do,这个不看了。如果不是调用PyRun_SimpleFileExFlags,
先调用PyImport_AddModule,interp->modules里插入了__main__,对我没啥用。maybe_pyc_file判断是否是pyc文件,比较了文件后缀,MagicNumber.
MAGIC_NUMBER是在Lib/importlib的_bootstrap_external.py文件里定义的,直接写在python文件里了。
如果像pyc文件,关闭文件句柄,重新打开文件,然后调用set_main_loader,再调用run_pyc_file。
如果不像pyc文件,调用PyRun_FileExFlags。
set_main_loader中通过loader_name,从importlib/_bootstrap_external中获取loader,源码为SourceFileLoader,pyc为SourcelessFileLoader,设置到__loader__
run_pyc_file,先比较MagicNumber是否匹配,然后PyMarshal_ReadLastObjectFromFile读取文件,其中的就不看了,不关心。PyEval_EvalCode应该是执行代码了,这个也不看了。
然后就完结了。
import 相关
第一篇里边的pymain_init_sys_path,调用pymain_get_importer,然后调用PyImport_GetImporter,然后调用get_path_importer,然后从path_hooks里找合适的import的方法进行导入
pymain_update_sys_path更新sys.path
config_init_module_search_paths里有个Py_GetPath(,这里比较乱,调用了几次_PyPathConfig_Init,我也没仔细看。_PyPathConfig_Calculate里应该是初始化的path,没仔细看。
第三方库的搜索路径添加在initsite,导入Lib/site.py模块,执行addusersitepackages里添加的
Read more...
Python源码特殊点分析2-参数解析分析
按照上一篇的流程
Modules/main.c里, pymain_main里pymain_init里的pymain_cmdline,是解析参数的。
初始化一个_PyCmdline,暂存参数的
line464
typedef struct {
wchar_t **argv;
int nwarnoption; /* Number of -W options */
wchar_t **warnoptions; /* -W options */
int nenv_warnoption; /* Number of PYTHONWARNINGS options */
wchar_t **env_warnoptions; /* PYTHONWARNINGS options */
int print_help; /* -h, -? options */
int print_version; /* -V option */
int bytes_warning; /* Py_BytesWarningFlag, -b */
int debug; /* Py_DebugFlag, -b, PYTHONDEBUG */
int inspect; /* Py_InspectFlag, -i, PYTHONINSPECT */
int interactive; /* Py_InteractiveFlag, -i */
int isolated; /* Py_IsolatedFlag, -I */
int optimization_level; /* Py_OptimizeFlag, -O, PYTHONOPTIMIZE */
int dont_write_bytecode; /* Py_DontWriteBytecodeFlag, -B, PYTHONDONTWRITEBYTECODE */
int no_user_site_directory; /* Py_NoUserSiteDirectory, -I, -s, PYTHONNOUSERSITE */
int no_site_import; /* Py_NoSiteFlag, -S */
int use_unbuffered_io; /* Py_UnbufferedStdioFlag, -u, PYTHONUNBUFFERED */
int verbosity; /* Py_VerboseFlag, -v, PYTHONVERBOSE */
int quiet_flag; /* Py_QuietFlag, -q */
const char *check_hash_pycs_mode; /* --check-hash-based-pycs */
#ifdef MS_WINDOWS
int legacy_windows_fs_encoding; /* Py_LegacyWindowsFSEncodingFlag,
PYTHONLEGACYWINDOWSFSENCODING */
int legacy_windows_stdio; /* Py_LegacyWindowsStdioFlag,
PYTHONLEGACYWINDOWSSTDIO */
#endif
} _PyCmdline;
然后调用cmdline_get_global_config,设置默认参数,默认参数都在 Python/pylifecycle.c 全为0.后面的cmdline_set_global_config是设置全局值的。两个是一对
然后调用pymain_cmdline_impl,里面调用pymain_read_conf,看了一下,主要在函数pymain_parse_cmdline_impl里面。其他的就不看了。
cmdline_get_env_flags是从环境拿值设置参数的。
pymain_init_core_argv是设置-c-m俩参数
没想到比较简单,没有细看,看的这些基本能达到目的了。有需要再补充
Read more...
Python源码特殊点分析1-入口函数分析
看Makefile.pre.in文件,入口函数在Programs/python.c文件里,发现main函数。
只调用的Modules/main.c的_Py_UnixMain函数,这里面初始化了_PyMain,用来保存参数,然后调用pymain_main
先调用pymain_init,后调用pymain_run_python,最后调用pymain_free.
pymain_init里,初始化_PyCoreConfig,大体看是pyvm的一些参数,先不关心。然后调用pymain_cmdline,大体看是解析参数的,不关心。pymain_init_stdio设置了stdin_is_interactive的值,这个可能有用,先记录。然后调用_Py_InitializeCore,里边调用_Py_InitializeCore_impl,里边各种初始化,不跟进去看了。
里边调用了一个_PyImport_Init,没啥,_PyImportHooks_Init,初始化了几个sys的参数,但是都是空值, sys.meta_path, sys.path_importer_cache,sys.path_hooks,这几个有用。
_Py_InitializeCore_impl最后调用了一个initimport(, sys.modules不知道在哪初始化的,可能有用,先不找了。importlib的导入,然后是_PyImportZip_Init.
_PyImportZip_Init在Python/import.c里,导入zipimport,path_hooks里加入了一个zipimport.zipimporter。搞定
回到 pymain_init, 最后调用了 pymain_init_python_main 和pymain_init_sys_path.
pymain_init_python_main调用_Py_InitializeMainInterpreter好像仅仅是重新设置了一下参数,这里没看懂,从自己复制了参数设置了args,有设置了一遍。没啥用先不管了。
pymain_init_sys_path这个看名字是要找的了, 第一个是zip或者目录导入,这个有用,记录一下,暂时不会用。从参数中获取sys.path的配置,最后调用pymain_update_sys_path插入到sys.path里边
pymain_run_python中,先调用pymain_header,pymain_import_readline,pymain_run_command,pymain_run_module, pymain_run_filename, pymain_repl.
pymain_header,如果是交互模式,打印了版本和操作系统。可以查到版本信息在Include/patchlevel.h
pymain_import_readline 为交互模式导入readline模块。pymain_run_command,直接执行的代码。pymain_run_module执行模块的.
pymain_run_filename 执行文件的。
pymain_repl 解析器模式
这部分就基本看完了,下边要单独看一下,启动参数解析的地方
Read more...
chrome 80版本以后,SameSite cookie验证跨站问题
这几天忙着写文档,一直看的线上环境的系统。今天启动本地测试环境,发现登录不成功。排查问题发现登录成功后,之后的请求没有携带cookie。然后我就仔细看了一下登录请求的参数。
发现在chorme header头的cookie里有一个黄色的小叹号,发现问题了。
this set-cookie didn't specify a "SameSite" attribute,然后变成默认Lax。(不能复制,懒得打英文了)
然后我搜索了一下Samesite定义,有三个值,None, Lax,Strict.这个参数是防止跨站攻击用的,因为测试环境,所以最方便就是跨站调试了。
然后我搜了一下flask源码,发现有这个设置,配置了一下发现不管用。然后放弃了。
找了一下怎么关闭chrome的这个选项,虽然别的网站可能有一点风险。自己调试的时候会方便点。
chrome://flags/#same-site-by-default-cookies
chrome://flags/#cookies-without-same-site-must-be-secure
这两项设置为Disabled,并重启浏览器
Read more...
python和c转换字体为像素矩阵
搞了一个像素游戏,很小的游戏。里边的文字也不多,找个像素字体太费劲,还有版权什么的信息,就想之前看过字体数据转像素矩阵的代码。搜了一下,发现直接有点阵字体,找了代码试了一下,完全可用。
我将字体转成像素矩阵后,可以直接按像素大小把字给画出来,非常方便了,就是计算稍微多一点。
HZK16字库是符合GB2312国家标准的16×16点阵字库,HZK16的GB2312-80支持的汉字有6763个,符号682个。其中一级汉字有 3755个,按声序排列,二级汉字有3008个,按偏旁部首排列。
我们在一些应用场合根本用不到这么多汉字字模,所以在应用时就可以只提取部分字体作为己用。 HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。
我们知道一个GB2312汉字是由两个字节编码的,范围为0xA1A1~0xFEFE。A1-A9为符号区,B0-F7为汉字区。每一个区有94个字符(注意:这只是编码的许可范围,不一定都有字型对应,比如符号区就有很多编码空白区域)。
下面以汉字「我」为例,介绍如何在HZK16文件中找到它对应的32个字节的字模数据。
前面说到一个汉字占两个字节,这两个中前一个字节为该汉字的区号,后一个字节为该字的位号。其中,每个区记录94个汉字,位号为该字在该区中的位置。所以要找到「我」在hzk16库中的位置就必须得到它的区码和位码。
区码:汉字的第一个字节-0xA0,因为汉字编码是从0xA0区开始的,所以文件最前面就是从0xA0区开始,要算出相对区码
位码:汉字的第二个字节-0xA0
这样我们就可以得到汉字在HZK16中的绝对偏移位置:offset = (94*(区码-1)+(位码-1))*32。
注解:
区码减1是因为数组是以0为开始而区号位号是以1为开始的
(94*(区号-1)+位号-1)是一个汉字字模占用的字节数
最后乘以32是因为汉字库文应从该位置起的32字节信息记录该字的字模信息(前面提到一个汉字要有32个字节显示)
#include
int main(void)
{
FILE* fphzk = NULL;
int i, j, k, offset;
int flag;
unsigned char buffer[32];
unsigned char word[3] = "我";
unsigned char key[8] = {
0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01
};
fphzk = fopen("hzk16", "rb");
if(fphzk == NULL){
fprintf(stderr, "error hzk16\n");
return 1;
}
offset = (94*(unsigned int)(word[0]-0xa0-1)+(word[1]-0xa0-1))*32;
fseek(fphzk, offset, SEEK_SET);
fread(buffer, 1, 32, fphzk);
for(k=0; k<32; k++){
printf("%02X ", buffer[k]);
}
for(k=0; k<16; k++){
for(j=0; j<2; j++){
for(i=0; i<8; i++){
flag = buffer[k*2+j]&key[i];
printf("%s", flag?"●":"○");
}
}
printf("\n");
}
fclose(fphzk);
fphzk = NULL;
return 0;
}
使用Python读取并显示的过程如下:
根据中文字符获取GB2312编码
通过GB2312编码计算该汉字在点阵字库中的区位和码位
通过区位和码位计算在点阵字库中的偏移量
基于偏移量获取该汉字的32个像素存储字节
解析像素字节获取点阵坐标信息
在对应的坐标显示信息位。如该像素点是否显示点亮
#!/usr/bin/python
#encoding: utf-8
import binascii
BYTE_COUNT_PER_FONT = 32
BYTE_COUNT_PER_ROW = 2
RECT_HEIGHT = 16
RECT_WIDTH = 16
KEYS = [0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01]
class FontRender(object):
def __init__(self, font_file="hzk16s",
rect_height=RECT_HEIGHT, rect_width=RECT_WIDTH, byte_count_per_row=BYTE_COUNT_PER_ROW):
self.font_file = font_file
self.rect_height = rect_height
self.rect_width = rect_width
self.byte_count_per_row = byte_count_per_row
self.__init_rect_list__()
def __init_rect_list__(self):
self.rect_list = [] * RECT_HEIGHT
for i in range(RECT_HEIGHT):
self.rect_list.append([] * RECT_WIDTH)
def get_font_area_index(self, txt, encoding='utf-8'):
if not isinstance(txt, unicode):
txt = txt.decode(encoding)
gb2312 = txt.encode('gb2312')
hex_str = binascii.b2a_hex(gb2312)
area = eval('0x' + hex_str[:2]) - 0xA0
index = eval('0x' + hex_str[2:]) - 0xA0
return area, index
def get_font_rect(self, area, index):
offset = (94 * (area-1) + (index-1)) * BYTE_COUNT_PER_FONT
btxt = None
with open(self.font_file, "rb") as f:
f.seek(offset)
btxt = f.read(32)
return btxt
def convert_font_rect(self, font_rect, ft=1, ff=0):
for k in range(len(font_rect) / self.byte_count_per_row):
row_list = self.rect_list[k]
for j in range(self.byte_count_per_row):
for i in range(8):
asc = binascii.b2a_hex(font_rect[k*2+j])
asc = eval('0x' + asc)
flag = asc & KEYS[i]
row_list.append(flag and ft or ff)
def render_font_rect(self, rect_list=None):
if not rect_list:
rect_list = self.rect_list
for row in rect_list:
for i in row:
if i:
print '●',
else:
print '○',
print
def convert(self, text, ft=None, ff=None, encoding='utf-8'):
if not isinstance(text, unicode):
text = text.decode(encoding)
for t in text:
area, index = self.get_font_area_index(t)
font_rect = self.get_font_rect(area, index)
self.convert_font_rect(font_rect, ft=ft, ff=ff)
def get_rect_info(self):
return self.rect_list
if '__main__' == __name__:
text = u'图麟'
fr = FontRender()
fr.convert(text, ft='/static/*', ff=0)
# print fr.get_rect_info()
fr.render_font_rect()
Read more...
JpaRepository自定义查询提示unexpected token:解决
查询语句里有个子查询,有个括号,然后就报错 unexpected token:(
发现这个异常没法查问题,就找了找跟JpaRepository子查询相关的。
有两种解决方法:
一种是@Query(value="select * from user where name like %?1" ,nativeQuery=true)
添加一个nativeQuery的参数
第二种方法是支持的是JPQL,但是我写的那个sql是不支持的。害
Read more...
Java日期时间操作相关Calendar,Date
Java真特么的坑,一边是对象Interger,一边是int等数据结构。之前感觉还挺有用的。现在我发现基本项目里都只用对象类型,然后我也就都用这个。
想实现的功能就是Date类型的数据的减两分钟,然后把秒置0的操作。
// 日历类型操作,Date的set方法现在都废弃不用了
Calendar calendar = Calendar.getInstance();
//设置秒为0
calendar.set(Calendar.SECOND, 0);
// 减去一分钟
calendar.add(Calendar.MINUTE, -1);
// 转成Date类型
Date timestamp = calendar.getTime();
Read more...
java里unix time 转换成Date对象
我要做一个东西,在spring boot的@requestBody里传了一个json数据,其中有个timestamp的字段,需要转换称java Date类型。
搞个半天我还以为成功了,然后突然发现是特么的1970年的年份,就感觉可能截断了。
搜了一下发现是需要毫秒的单位,乘了1000还是不行,后来又找了很多方法,以为只能重新写json字段解析的反序列化函数单独搞。
然后发现一个是回答的必须long型的,然后试了一下就成功了。。。
java.util.Date time=new java.util.Date((long)timeStamp*1000);
time=new java.util.Date(timeStamp*1000L);
Date time = new Date(TimeUnit.MILLISECONDS.convert(unixTimestamp, TimeUnit.SECONDS));
直接改了set属性的方法就可用了
Read more...