From 932e310f9d003b2f8bf9c7efd39d112c9dc50d0e Mon Sep 17 00:00:00 2001 From: mayx Date: Fri, 11 Sep 2026 10:30:53 +0000 Subject: [PATCH] Update 6 files - /_tools/blogquine.py - /_tools/serv00_post-receive - /_tools/envs_post-receive - /_data/other_repo_list.csv - /deploy.sh - /.readthedocs.yaml --- .readthedocs.yaml | 2 +- _data/other_repo_list.csv | 17 ------- _tools/blogquine.py | 94 +++++++++++++++++++------------------- _tools/envs_post-receive | 4 +- _tools/serv00_post-receive | 5 +- deploy.sh | 2 +- 6 files changed, 53 insertions(+), 71 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 6ef414a..1505147 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -12,5 +12,5 @@ build: - mkdir Mabbs - curl -L -o Mabbs/README.md https://github.com/Mabbs/Mabbs/raw/main/README.md - bundle exec jekyll build --destination $READTHEDOCS_OUTPUT/html - - python3 _tools/blogquine.py $READTHEDOCS_OUTPUT/html MayxBlog.7z + - python3 _tools/blogquine.py --src-dir blog --quine-dir blog $READTHEDOCS_OUTPUT/html MayxBlog.7z - mv MayxBlog.7z $READTHEDOCS_OUTPUT/html \ No newline at end of file diff --git a/_data/other_repo_list.csv b/_data/other_repo_list.csv index 2d55518..9c1b097 100644 --- a/_data/other_repo_list.csv +++ b/_data/other_repo_list.csv @@ -44,7 +44,6 @@ https://gitea.sciotech.cn/mayx/blog https://gitea.micro-stack.org/mayx/blog http://106.54.211.95:3000/mayx/blog http://172.172.102.93:3000/mayx/blog -http://120.24.249.56/mayx/blog http://120.211.66.170:8418/mayx/blog http://209.127.59.74:3000/mayx/blog http://121.41.2.71:3000/mayx/blog @@ -109,7 +108,6 @@ http://36.138.125.206:3000/mayx/blog_cn https://git.crystalyx.net/mayx/blog https://gitea.grxe.io/mayx/blog https://gitea.accept.dev.dbf.nl/mayx/blog -https://git.jhossain.online/mayx/blog https://barhoum-lab.fr/mayx/blog http://gitea.snailtrack.cn/mayx/blog_cn https://code.draussenfunker.de/mayx/blog_cn @@ -120,7 +118,6 @@ https://gitea.malxte.de/mayx/blog https://git.nozora.top/mayx/blog https://git.edenit.co.kr/mayx/blog https://git.catgirlsneed.homes/mayx/blog -http://47.108.255.216:3000/mayx/blog_cn http://210.75.240.13:3000/mayx/blog_cn http://git.hbg99.com:8080/mayx/blog_cn https://git.eplg.services/mayx/blog @@ -135,8 +132,6 @@ http://124.71.169.183:3000/mayx/blog https://git.msoucy.me/mayx/blog http://140.120.108.238:49308/mayx/blog http://git.chilidoginteractive.com:3000/mayx/blog_cn -http://git.uhfdemo.com/mayx/blog_cn -http://43.143.142.38:7001/mayx/blog_cn https://git.panda-number.one/mayx/blog https://datacortex.pl/mayx/blog https://gitea.keymantek.com:777/mayx/blog @@ -173,7 +168,6 @@ https://gitbucket.aint-no.info/mayx/blog https://git.wikeo.ovh/mayx/blog https://devcraft-dot.tech/mayx/blog https://a-t-g.ru/mayx/blog -http://qiubei-git.cn/mayx/blog_cn http://wangbeibei.xyz:6002/mayx/blog http://8.141.82.163:20000/mayx/blog_cn http://81.70.30.91:3000/mayx/blog_cn @@ -181,7 +175,6 @@ http://yidaima.cn:6008/mayx/blog_cn http://8.148.31.14:3000/mayx/blog_cn http://58.17.14.95:8001/mayx/blog_cn http://39.99.175.172:8000/mayx/blog_cn -https://git.newnaturalphilosophy.org/mayx/blog_cn https://gitea.detr.top/mayx/blog_cn http://175.178.193.35:3000/mayx/blog_cn http://gitea.jb1000.com:88/mayx/blog_cn @@ -194,15 +187,12 @@ http://jdunlap.com/mayx/blog https://git.silica.codes/mayx/blog https://www.vitelgrid.app/mayx/blog https://gitruhub.ru/mayx/blog -https://git.linuxposting.xyz/mayx/blog https://git.techworkshop42.ru/mayx/blog http://speedyfox.app/mayx/blog https://mjjtop.com/mayx/blog https://forgejo.wanderingmonster.dev/mayx/blog -http://v.udvip.com/mayx/blog https://git.edavmig.ru/mayx/blog https://gitea.ns5001k.sigma2.no/mayx/blog -https://fzsimo.com/mayx/blog https://git.hemangvyas.com/mayx/blog https://gitav.ru/mayx/blog https://git.sakuzyo.net/mayx/blog @@ -210,16 +200,12 @@ http://www.365rise.top/mayx/blog https://git.ifuntanhub.dev/mayx/blog https://git.aptcloud.ru/mayx/blog https://gitlab.dev.genai-team.ru/mayx/blog -https://d.roxyipt.com/mayx/blog https://git.umervtilte.lol/mayx/blog https://git.bitpak.ru/mayx/blog https://git.amamedis.de/mayx/blog https://git.kunstglass.de/mayx/blog https://git.kry008.xyz/mayx/blog -https://gitea.onfing.ir/mayx/blog -https://gitea.augeo.dev/mayx/blog_cn http://36.213.200.127:23000/mayx/blog_cn -http://20.219.0.85:3000/mayx/blog_cn https://git.niisse.net/mayx/blog_cn https://gitea.yfandxt.asia/mayx/blog_cn http://81.69.57.215:3000/mayx/blog_cn @@ -227,7 +213,6 @@ https://git.dglyoo.com/mayx/blog_cn http://c8202.top:3000/mayx/blog_cn http://nojava.fun/mayx/blog_cn https://gitimn.com/mayx/blog -http://git.zapthisblog.com/mayx/blog https://git.resacachile.cl/mayx/blog https://git.vycsucre.gob.ve/mayx/blog https://newtrafficworksgit.online/mayx/blog @@ -236,10 +221,8 @@ https://git.albiobola.nl/mayx/blog http://gitea.amm.ar/mayx/blog https://git.83channel.net/mayx/blog https://git.zeppone.com/mayx/blog -https://git.telecom.quest/mayx/blog https://repo.paperless.rs/mayx/blog https://urbal.dev/mayx/blog https://git.pwarde.nl/mayx/blog -https://presko.info/mayx/blog https://bolha.dev/mayx/blog https://git.checkerwars.com/mayx/blog diff --git a/_tools/blogquine.py b/_tools/blogquine.py index 6fe32d3..fb1842b 100644 --- a/_tools/blogquine.py +++ b/_tools/blogquine.py @@ -7,12 +7,13 @@ import lzma import os import struct import sys +import time # ============================================================ # Part 1: 最小 LZMA1 range coder(只做编码) # # 设计目标:只实现 quine 构造需要的 token:literal / match / rep0-match / -# end-marker,编码字节与历史内容无关(track=False 时不维护输出历史), +# end-marker,编码字节与历史内容无关(不维护输出历史), # 这是"先算结构、后填数据"两阶段装配的基础。 # 参考:Igor Pavlov 的 LZMA SDK (LzmaEnc.c / LzmaDec.c)。 # ============================================================ @@ -125,14 +126,12 @@ def _pos_slot_and_bits(d): class LzmaEncoder: - def __init__(self, lc=3, lp=0, pb=2, track=False): + def __init__(self, lc=3, lp=0, pb=2): """ - track=True 时维护输出假历史(literal/match 复制内容),用于一般用途; - quine 构造用 track=False:match 的编码字节只取决于 (dist,len,pos), - 与历史内容无关,可跳过 37MB 级假历史的逐字节复制。 + 不维护输出假历史:match 的编码字节只取决于 (dist,len,pos),与历史内容无关, + 因此 quine 构造(先算结构、后填数据)不需要逐字节复制假历史。 """ self.lc, self.lp, self.pb = lc, lp, pb - self.track = track self.pos_mask = (1 << pb) - 1 self.lp_mask = (1 << lp) - 1 self.rc = RangeEncoder() @@ -184,13 +183,6 @@ class LzmaEncoder: self.rc.encode_bit(choice, 1, 1) self.rc.bittree_encode(high, 0, kNumHighLenBits, l - kNumMidLenSymbols) - def _copy_out(self, dist, length): - """track=True 时把 match 的输出复制进假历史。""" - src = len(self.out) - dist - for i in range(length): - self.out.append(self.out[src + i]) - self.prev_byte = self.out[-1] - # ---------- 对外 token ---------- def literal(self, b): if self.state >= kNumLitStates: @@ -240,36 +232,19 @@ class LzmaEncoder: self.state = 7 if self.state < kNumLitStates else 10 self.reps[3], self.reps[2], self.reps[1], self.reps[0] = \ self.reps[2], self.reps[1], self.reps[0], d - if self.track: - self._copy_out(dist, length) self.pos += length - def rep_match(self, length, rep_idx=0): + def rep_match(self, length): + """rep0 匹配:只使用最近一次 match 的距离(quine 构造只需要这种)。""" assert kMatchMinLen <= length <= 273, length ps = self.pos_state self.rc.encode_bit(self.p_is_match, (self.state << kNumPosBitsMax) + ps, 1) self.rc.encode_bit(self.p_is_rep, self.state, 1) - if rep_idx == 0: - self.rc.encode_bit(self.p_is_rep_g0, self.state, 0) - self.rc.encode_bit(self.p_rep0_long, (self.state << kNumPosBitsMax) + ps, 1) - else: - self.rc.encode_bit(self.p_is_rep_g0, self.state, 1) - if rep_idx == 1: - self.rc.encode_bit(self.p_is_rep_g1, self.state, 0) - else: - self.rc.encode_bit(self.p_is_rep_g1, self.state, 1) - self.rc.encode_bit(self.p_is_rep_g2, self.state, 0 if rep_idx == 2 else 1) - if rep_idx == 3: - self.reps[3] = self.reps[2] - self.reps[2] = self.reps[1] - self.reps[1] = self.reps[0] - self.reps[0] = self.reps[rep_idx] + self.rc.encode_bit(self.p_is_rep_g0, self.state, 0) + self.rc.encode_bit(self.p_rep0_long, (self.state << kNumPosBitsMax) + ps, 1) self._encode_len(self.p_rep_len_choice, self.p_rep_len_low, self.p_rep_len_mid, self.p_rep_len_high, length) - dist = self.reps[0] + 1 self.state = 8 if self.state < kNumLitStates else 11 - if self.track: - self._copy_out(dist, length) self.pos += length def end(self): @@ -293,6 +268,8 @@ class LzmaEncoder: SIG = b"7z\xbc\xaf'\x1c" VER = b"\x00\x04" MAX_MATCH = 273 # LZMA1 单个 match 长度上限 +# FILETIME 纪元(1601-01-01)与 Unix 纪元(1970-01-01)之差,单位 100ns +FILETIME_EPOCH_OFFSET = 116444736000000000 def crc32(b, v=0): @@ -356,7 +333,7 @@ def dict_prop_for(maxdist): def lzma_chunk(tokens, out_pos): """编一个 LZMA chunk(0xC0: state+props reset,无 dict reset,无 end marker)。 - match token 的编码字节只取决于 (dist,len,pos),与历史内容无关(track=False)。""" + match token 的编码字节只取决于 (dist,len,pos),与历史内容无关(不维护输出历史)。""" enc = LzmaEncoder() enc.pos = out_pos total = 0 @@ -365,7 +342,7 @@ def lzma_chunk(tokens, out_pos): enc.match(t[1], t[2]) total += t[2] else: - enc.rep_match(t[1], 0) + enc.rep_match(t[1]) total += t[1] data = enc.finish() assert total - 1 < 65536 and len(data) - 1 < 65536 @@ -375,28 +352,43 @@ def lzma_chunk(tokens, out_pos): class BlogQuine: - def __init__(self, root, quine_name="MayxBlog.7z", seed_name="_quine_seed.bin"): + def __init__(self, root, quine_name="MayxBlog.7z", seed_name="_quine_seed.bin", + src_dir=""): self.quine_name = quine_name self.seed_name = seed_name + # 源文件在归档内的前缀目录(如 "src" → 归档内出现 src/hello.txt);"" = 根目录 + self.src_dir = src_dir.strip("/") + # 程序运行时间 → FILETIME(1601-01-01 起 100ns 计数), + # 本次运行只取一次,作为归档内所有文件条目的修改时间字段。 + self.mtime_ft = time.time_ns() // 100 + FILETIME_EPOCH_OFFSET # walk_entries: walk 顺序(父先子后、同级 dirs 在前)的 (rel, is_dir)。 # 子流顺序 = 非空条目在此列表中的顺序,必须自始至终保持同一顺序。 self.walk_entries = [] self.files = [] # (relpath, data bytes),顺序 = walk_entries 中文件序 + # src_dir 的各级父目录先作为目录条目挂入(父先子后), + # 这样归档结构完整:如 src_dir="public/src" → "public"、"public/src" + if self.src_dir: + parts = self.src_dir.split("/") + for i in range(1, len(parts) + 1): + self.walk_entries.append(("/".join(parts[:i]), True)) for dirpath, dirnames, filenames in os.walk(root): dirnames.sort() for dn in dirnames: rel = os.path.relpath(os.path.join(dirpath, dn), root) - self.walk_entries.append((rel.replace(os.sep, "/"), True)) + self.walk_entries.append((self._prefixed(rel), True)) for fn in sorted(filenames): p = os.path.join(dirpath, fn) rel = os.path.relpath(p, root).replace(os.sep, "/") with open(p, "rb") as fp: data = fp.read() - self.walk_entries.append((rel.replace(os.sep, "/"), False)) - self.files.append((rel.replace(os.sep, "/"), data)) + self.walk_entries.append((self._prefixed(rel), False)) + self.files.append((self._prefixed(rel), data)) self.dirs = [r for r, isd in self.walk_entries if isd] self.content = b"".join(data for _, data in self.files) + def _prefixed(self, rel): + return self.src_dir + "/" + rel if self.src_dir else rel + def entries(self): ent = [{"name": self.seed_name, "dir": False}] ent += [{"name": r, "dir": isd} for r, isd in self.walk_entries] @@ -428,6 +420,9 @@ class BlogQuine: if e["dir"]: bits[i // 8] |= 1 << (7 - i % 8) h += b"\x0e" + varint(len(bits)) + bytes(bits) + # kMTime:所有条目(文件与目录)写入修改时间(= 本次程序运行时间) + mt = b"\x01\x00" + struct.pack("