gaskill 2.15.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gaskill/__init__.py +13 -0
- gaskill/gaskill/__init__.py +28 -0
- gaskill/gaskill/_roll.py +169 -0
- gaskill/gaskill/abc.py +73 -0
- gaskill/gaskill/builtins_plus.py +73 -0
- gaskill/gaskill/pickle.py +318 -0
- gaskill/gaskill/sbase64.py +287 -0
- gaskill/gaskill/scache.py +448 -0
- gaskill/gaskill/scolor.py +388 -0
- gaskill/gaskill/scrypt.py +209 -0
- gaskill/gaskill/scurve.py +387 -0
- gaskill/gaskill/seasing.py +259 -0
- gaskill/gaskill/sevent.py +218 -0
- gaskill/gaskill/sformat.py +591 -0
- gaskill/gaskill/sfrac.py +403 -0
- gaskill/gaskill/sfsm.py +209 -0
- gaskill/gaskill/sgeometry.py +790 -0
- gaskill/gaskill/sgeometry3d.py +371 -0
- gaskill/gaskill/sinterp.py +168 -0
- gaskill/gaskill/smath.py +1111 -0
- gaskill/gaskill/smatrix.py +605 -0
- gaskill/gaskill/snoise.py +305 -0
- gaskill/gaskill/solver.py +176 -0
- gaskill/gaskill/spathfinder.py +547 -0
- gaskill/gaskill/spool.py +201 -0
- gaskill/gaskill/squaternion.py +439 -0
- gaskill/gaskill/srandom.py +280 -0
- gaskill/gaskill/ssaver.py +303 -0
- gaskill/gaskill/sstat.py +463 -0
- gaskill/gaskill/sui.py +474 -0
- gaskill/gaskill/svector.py +756 -0
- gaskill/gaskill/type.py +133 -0
- gaskill/plus/__init__.py +12 -0
- gaskill/plus/bmp.py +436 -0
- gaskill/plus/database.py +174 -0
- gaskill/plus/decimal.py +462 -0
- gaskill/plus/ecc.py +160 -0
- gaskill/plus/hashlib.py +185 -0
- gaskill/plus/pickle.py +320 -0
- gaskill/plus/poly.py +426 -0
- gaskill/plus/radical.py +608 -0
- gaskill/plus/re.py +511 -0
- gaskill/plus/tester.py +500 -0
- gaskill/plus/tree.py +180 -0
- gaskill/plus/zip.py +233 -0
- gaskill/test/__init__.py +6 -0
- gaskill/test/test_all.py +11 -0
- gaskill/test/test_decimal.py +656 -0
- gaskill/test/test_frac.py +487 -0
- gaskill/test/test_radical.py +243 -0
- gaskill/test/test_saver.py +152 -0
- gaskill/test/test_smath.py +321 -0
- gaskill-2.15.2.dist-info/METADATA +43 -0
- gaskill-2.15.2.dist-info/RECORD +57 -0
- gaskill-2.15.2.dist-info/WHEEL +5 -0
- gaskill-2.15.2.dist-info/licenses/LICENSE +21 -0
- gaskill-2.15.2.dist-info/top_level.txt +1 -0
gaskill/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# ruff: noqa: F403
|
|
2
|
+
from .gaskill import *
|
|
3
|
+
from .plus import *
|
|
4
|
+
print(r"""
|
|
5
|
+
_ _ _
|
|
6
|
+
_ __ ___ | (_) |__
|
|
7
|
+
| '_ ` _ \| | | '_ \
|
|
8
|
+
| | | | | | | | |_) |
|
|
9
|
+
|_| |_| |_|_|_|_.__/ """)
|
|
10
|
+
print("Hello from mlib. It isn't a serious library. Welcome to raise PRs. If you want to know the rules, please see PRlicense.")
|
|
11
|
+
__PRlicense__ = "Do not use any C or Rust extensions, and ensure neither exists in the dependency tree."
|
|
12
|
+
__license__ = "MIT"
|
|
13
|
+
__author__ = "everyone"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# ruff: noqa: F403
|
|
2
|
+
from .smath import *
|
|
3
|
+
from .sfrac import *
|
|
4
|
+
from .svector import *
|
|
5
|
+
from .smatrix import *
|
|
6
|
+
from .squaternion import *
|
|
7
|
+
from .srandom import *
|
|
8
|
+
from .scolor import *
|
|
9
|
+
from .sgeometry import *
|
|
10
|
+
from .sgeometry3d import *
|
|
11
|
+
from .scurve import *
|
|
12
|
+
from .seasing import *
|
|
13
|
+
from .sinterp import *
|
|
14
|
+
from .scache import *
|
|
15
|
+
from .sevent import *
|
|
16
|
+
from .sfsm import *
|
|
17
|
+
from .spool import *
|
|
18
|
+
from .spathfinder import *
|
|
19
|
+
from .ssaver import *
|
|
20
|
+
from .sui import *
|
|
21
|
+
from .sformat import *
|
|
22
|
+
from .snoise import *
|
|
23
|
+
from .scrypt import *
|
|
24
|
+
from .solver import *
|
|
25
|
+
from .sstat import *
|
|
26
|
+
from .sbase64 import *
|
|
27
|
+
from .abc import *
|
|
28
|
+
from .pickle import *
|
gaskill/gaskill/_roll.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
def parse_dice(expr):
|
|
2
|
+
expr = expr.replace(' ', '')
|
|
3
|
+
|
|
4
|
+
# 按加减号分割表达式
|
|
5
|
+
parts = _split_expression(expr)
|
|
6
|
+
|
|
7
|
+
result = {
|
|
8
|
+
'expr': expr,
|
|
9
|
+
'parts': [],
|
|
10
|
+
'total': 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
for part in parts:
|
|
14
|
+
if 'd' in part.lower():
|
|
15
|
+
part_result = _parse_single_dice(part)
|
|
16
|
+
else:
|
|
17
|
+
part_result = _parse_number(part)
|
|
18
|
+
|
|
19
|
+
result['parts'].append(part_result)
|
|
20
|
+
result['total'] += part_result['value']
|
|
21
|
+
|
|
22
|
+
return result
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _split_expression(expr):
|
|
26
|
+
parts = []
|
|
27
|
+
current = ''
|
|
28
|
+
|
|
29
|
+
for i, char in enumerate(expr):
|
|
30
|
+
if char in '+-' and i > 0:
|
|
31
|
+
parts.append(current)
|
|
32
|
+
current = char
|
|
33
|
+
else:
|
|
34
|
+
current += char
|
|
35
|
+
|
|
36
|
+
if current:
|
|
37
|
+
parts.append(current)
|
|
38
|
+
|
|
39
|
+
return parts
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _parse_single_dice(expr):
|
|
43
|
+
d_pos = expr.lower().find('d')
|
|
44
|
+
|
|
45
|
+
# 解析骰子数量
|
|
46
|
+
count_str = expr[:d_pos]
|
|
47
|
+
count = int(count_str) if count_str else 1
|
|
48
|
+
|
|
49
|
+
# 解析剩余部分
|
|
50
|
+
rest = expr[d_pos+1:]
|
|
51
|
+
|
|
52
|
+
# 初始化结果
|
|
53
|
+
result = {
|
|
54
|
+
'expr': expr,
|
|
55
|
+
'type': 'dice',
|
|
56
|
+
'count': count,
|
|
57
|
+
'sides': 0,
|
|
58
|
+
'modifier': None,
|
|
59
|
+
'modifier_value': 0,
|
|
60
|
+
'explode': None,
|
|
61
|
+
'bonus': 0,
|
|
62
|
+
'value': 0 # 占位,实际掷骰时计算
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
i = 0
|
|
66
|
+
# 读骰子面数
|
|
67
|
+
sides_str = ''
|
|
68
|
+
while i < len(rest) and rest[i].isdigit():
|
|
69
|
+
sides_str += rest[i]
|
|
70
|
+
i += 1
|
|
71
|
+
|
|
72
|
+
result['sides'] = int(sides_str)
|
|
73
|
+
|
|
74
|
+
# 读修饰符
|
|
75
|
+
if i < len(rest) and rest[i].isalpha():
|
|
76
|
+
mod = rest[i]
|
|
77
|
+
i += 1
|
|
78
|
+
if i < len(rest) and rest[i].isalpha():
|
|
79
|
+
mod += rest[i]
|
|
80
|
+
i += 1
|
|
81
|
+
result['modifier'] = mod
|
|
82
|
+
|
|
83
|
+
# 读修饰符数值
|
|
84
|
+
mod_val_str = ''
|
|
85
|
+
while i < len(rest) and rest[i].isdigit():
|
|
86
|
+
mod_val_str += rest[i]
|
|
87
|
+
i += 1
|
|
88
|
+
result['modifier_value'] = int(mod_val_str) if mod_val_str else 1
|
|
89
|
+
|
|
90
|
+
# 读爆炸骰
|
|
91
|
+
if i < len(rest) and rest[i] == 'x':
|
|
92
|
+
i += 1
|
|
93
|
+
explode_str = ''
|
|
94
|
+
while i < len(rest) and rest[i].isdigit():
|
|
95
|
+
explode_str += rest[i]
|
|
96
|
+
i += 1
|
|
97
|
+
result['explode'] = int(explode_str)
|
|
98
|
+
|
|
99
|
+
# 读加减
|
|
100
|
+
if i < len(rest) and rest[i] in '+-':
|
|
101
|
+
result['bonus'] = int(rest[i:])
|
|
102
|
+
|
|
103
|
+
return result
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _parse_number(expr):
|
|
107
|
+
return {
|
|
108
|
+
'expr': expr,
|
|
109
|
+
'type': 'number',
|
|
110
|
+
'value': int(expr)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def roll_dice(parse_result, random_func):
|
|
115
|
+
if parse_result['type'] == 'number':
|
|
116
|
+
return parse_result['value']
|
|
117
|
+
|
|
118
|
+
# 先掷出所有骰子
|
|
119
|
+
rolls = [random_func(1, parse_result['sides'])
|
|
120
|
+
for _ in range(parse_result['count'])]
|
|
121
|
+
|
|
122
|
+
# 应用爆炸骰
|
|
123
|
+
if parse_result['explode']:
|
|
124
|
+
rolls = _apply_explode(rolls, parse_result['sides'],
|
|
125
|
+
parse_result['explode'], random_func)
|
|
126
|
+
|
|
127
|
+
# 应用修饰符
|
|
128
|
+
if parse_result['modifier'] == 'kh':
|
|
129
|
+
rolls = sorted(rolls, reverse=True)[:parse_result['modifier_value']]
|
|
130
|
+
elif parse_result['modifier'] == 'kl':
|
|
131
|
+
rolls = sorted(rolls)[:parse_result['modifier_value']]
|
|
132
|
+
elif parse_result['modifier'] == 'd':
|
|
133
|
+
rolls = sorted(rolls)[parse_result['modifier_value']:]
|
|
134
|
+
|
|
135
|
+
return sum(rolls) + parse_result['bonus']
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _apply_explode(rolls, sides, target, random_func):
|
|
139
|
+
"""递归应用爆炸骰"""
|
|
140
|
+
result = rolls.copy()
|
|
141
|
+
for roll in rolls:
|
|
142
|
+
if roll == target:
|
|
143
|
+
extra = random_func(1, sides)
|
|
144
|
+
result.append(extra)
|
|
145
|
+
if extra == target: # 递归爆炸
|
|
146
|
+
result.extend(_apply_explode([extra], sides, target, random_func))
|
|
147
|
+
return result
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def get_dice_info(expr):
|
|
151
|
+
result = parse_dice(expr)
|
|
152
|
+
info = {
|
|
153
|
+
'expr': expr,
|
|
154
|
+
'dice_count': 0,
|
|
155
|
+
'total_sides': 0,
|
|
156
|
+
'has_modifier': False,
|
|
157
|
+
'has_explode': False
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
for part in result['parts']:
|
|
161
|
+
if part['type'] == 'dice':
|
|
162
|
+
info['dice_count'] += part['count']
|
|
163
|
+
info['total_sides'] += part['sides'] * part['count']
|
|
164
|
+
if part['modifier']:
|
|
165
|
+
info['has_modifier'] = True
|
|
166
|
+
if part['explode']:
|
|
167
|
+
info['has_explode'] = True
|
|
168
|
+
|
|
169
|
+
return info
|
gaskill/gaskill/abc.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gaskill.abc - 抽象基类模块(零依赖)
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ABCMeta(type):
|
|
7
|
+
"""元类:自动收集抽象方法"""
|
|
8
|
+
|
|
9
|
+
def __new__(cls, name, bases, dct):
|
|
10
|
+
# 1. 从所有父类继承抽象方法
|
|
11
|
+
abstract_methods = set()
|
|
12
|
+
for base in bases:
|
|
13
|
+
if hasattr(base, '_abstract_methods'):
|
|
14
|
+
abstract_methods.update(base._abstract_methods)
|
|
15
|
+
|
|
16
|
+
# 2. 处理当前类定义的方法
|
|
17
|
+
for key, value in dct.items():
|
|
18
|
+
# 如果当前类显式定义了抽象方法(有 _is_abstract 标记)
|
|
19
|
+
if hasattr(value, '_is_abstract') and value._is_abstract:
|
|
20
|
+
abstract_methods.add(key)
|
|
21
|
+
else:
|
|
22
|
+
# 如果当前类定义了具体实现(没有 _is_abstract 标记)
|
|
23
|
+
# 并且该方法在抽象方法集合中,则移除(即实现了该抽象方法)
|
|
24
|
+
if key in abstract_methods:
|
|
25
|
+
abstract_methods.remove(key)
|
|
26
|
+
|
|
27
|
+
# 3. 存储最终的抽象方法集合
|
|
28
|
+
dct['_abstract_methods'] = frozenset(abstract_methods)
|
|
29
|
+
return super().__new__(cls, name, bases, dct)
|
|
30
|
+
|
|
31
|
+
def __call__(cls, *args, **kwargs):
|
|
32
|
+
"""实例化时检查抽象方法是否都已实现"""
|
|
33
|
+
if cls._abstract_methods:
|
|
34
|
+
methods = ', '.join(cls._abstract_methods)
|
|
35
|
+
raise TypeError(
|
|
36
|
+
f"Can't instantiate abstract class {cls.__name__} "
|
|
37
|
+
f"with abstract methods {methods}"
|
|
38
|
+
)
|
|
39
|
+
return super().__call__(*args, **kwargs)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ABC(metaclass=ABCMeta):
|
|
43
|
+
"""抽象基类"""
|
|
44
|
+
|
|
45
|
+
def __init__(self):
|
|
46
|
+
|
|
47
|
+
if type(self) is ABC:
|
|
48
|
+
raise TypeError(
|
|
49
|
+
"Can't instantiate abstract class ABC with abstract methods")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def abstractmethod(func):
|
|
53
|
+
"""装饰器:标记抽象方法"""
|
|
54
|
+
func._is_abstract = True
|
|
55
|
+
return func
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def abstractclassmethod(func):
|
|
59
|
+
"""装饰器:标记抽象类方法"""
|
|
60
|
+
func._is_abstract = True
|
|
61
|
+
return classmethod(func)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def abstractstaticmethod(func):
|
|
65
|
+
"""装饰器:标记抽象静态方法"""
|
|
66
|
+
func._is_abstract = True
|
|
67
|
+
return staticmethod(func)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def abstractproperty(func):
|
|
71
|
+
"""装饰器:标记抽象属性"""
|
|
72
|
+
func._is_abstract = True
|
|
73
|
+
return property(func)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from .abc import ABC, abstractmethod
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Iterable(ABC):
|
|
5
|
+
@abstractmethod
|
|
6
|
+
def __iter__(self):
|
|
7
|
+
while False:
|
|
8
|
+
yield None
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def __subclasshook__(cls, C):
|
|
12
|
+
if cls is Iterable:
|
|
13
|
+
if any("__iter__" in B.__dict__ for B in C.__mro__):
|
|
14
|
+
return True
|
|
15
|
+
return NotImplemented
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class DefaultDict:
|
|
19
|
+
"""带默认值的字典"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, default_factory=None, **kwargs):
|
|
22
|
+
self._data = {}
|
|
23
|
+
self._default_factory = default_factory
|
|
24
|
+
self.update(kwargs)
|
|
25
|
+
|
|
26
|
+
def __getitem__(self, key):
|
|
27
|
+
try:
|
|
28
|
+
return self._data[key]
|
|
29
|
+
except KeyError:
|
|
30
|
+
if self._default_factory is None:
|
|
31
|
+
raise
|
|
32
|
+
value = self._default_factory()
|
|
33
|
+
self._data[key] = value
|
|
34
|
+
return value
|
|
35
|
+
|
|
36
|
+
def __setitem__(self, key, value):
|
|
37
|
+
self._data[key] = value
|
|
38
|
+
|
|
39
|
+
def __delitem__(self, key):
|
|
40
|
+
del self._data[key]
|
|
41
|
+
|
|
42
|
+
def __iter__(self):
|
|
43
|
+
return iter(self._data)
|
|
44
|
+
|
|
45
|
+
def __len__(self):
|
|
46
|
+
return len(self._data)
|
|
47
|
+
|
|
48
|
+
def __repr__(self):
|
|
49
|
+
return f"DefaultDict({self._default_factory}, {self._data})"
|
|
50
|
+
|
|
51
|
+
def update(self, other):
|
|
52
|
+
self._data.update(other)
|
|
53
|
+
|
|
54
|
+
def get(self, key, default=None):
|
|
55
|
+
return self._data.get(key, default)
|
|
56
|
+
|
|
57
|
+
def pop(self, key, default=None):
|
|
58
|
+
return self._data.pop(key, default)
|
|
59
|
+
|
|
60
|
+
def keys(self):
|
|
61
|
+
return self._data.keys()
|
|
62
|
+
|
|
63
|
+
def values(self):
|
|
64
|
+
return self._data.values()
|
|
65
|
+
|
|
66
|
+
def items(self):
|
|
67
|
+
return self._data.items()
|
|
68
|
+
|
|
69
|
+
def copy(self):
|
|
70
|
+
return DefaultDict(self._default_factory, **self._data)
|
|
71
|
+
|
|
72
|
+
def clear(self):
|
|
73
|
+
self._data.clear()
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
def pack_float(num):
|
|
2
|
+
"""将浮点数转换为二进制表示"""
|
|
3
|
+
|
|
4
|
+
if num == 0:
|
|
5
|
+
if str(num) == "0.0":
|
|
6
|
+
return '0' * 64
|
|
7
|
+
else:
|
|
8
|
+
return '1' + '0' * 63
|
|
9
|
+
|
|
10
|
+
if num == float("inf") or num == float("-inf"):
|
|
11
|
+
return '0' + '1'*11 + '0'*52 if num > 0 else '1' + '1'*11 + '0'*52
|
|
12
|
+
|
|
13
|
+
if str(num) == 'nan':
|
|
14
|
+
return '0' + '1'*11 + '1' + '0'*51
|
|
15
|
+
|
|
16
|
+
sign = '0' if num > 0 else '1'
|
|
17
|
+
num = abs(num)
|
|
18
|
+
|
|
19
|
+
int_part = int(num)
|
|
20
|
+
frac_part = num - int_part
|
|
21
|
+
|
|
22
|
+
int_bits = []
|
|
23
|
+
if int_part == 0:
|
|
24
|
+
int_bits = ['0']
|
|
25
|
+
else:
|
|
26
|
+
temp = int_part
|
|
27
|
+
while temp > 0:
|
|
28
|
+
int_bits.append(str(temp % 2))
|
|
29
|
+
temp //= 2
|
|
30
|
+
int_bits.reverse()
|
|
31
|
+
|
|
32
|
+
frac_bits = []
|
|
33
|
+
temp = frac_part
|
|
34
|
+
count = 0
|
|
35
|
+
while temp > 0 and count < 60:
|
|
36
|
+
temp *= 2
|
|
37
|
+
if temp >= 1:
|
|
38
|
+
frac_bits.append('1')
|
|
39
|
+
temp -= 1
|
|
40
|
+
else:
|
|
41
|
+
frac_bits.append('0')
|
|
42
|
+
count += 1
|
|
43
|
+
binary = int_bits + frac_bits
|
|
44
|
+
|
|
45
|
+
first_one = -1
|
|
46
|
+
for i, bit in enumerate(binary):
|
|
47
|
+
if bit == '1':
|
|
48
|
+
first_one = i
|
|
49
|
+
break
|
|
50
|
+
|
|
51
|
+
if first_one == -1: # 全是0
|
|
52
|
+
return '0' * 64
|
|
53
|
+
|
|
54
|
+
if first_one < len(int_bits):
|
|
55
|
+
exponent = len(int_bits) - first_one - 1
|
|
56
|
+
else:
|
|
57
|
+
exponent = - (first_one - len(int_bits) + 1)
|
|
58
|
+
|
|
59
|
+
biased_exponent = exponent + 1023
|
|
60
|
+
exp_bits = bin(biased_exponent)[2:].zfill(11)
|
|
61
|
+
|
|
62
|
+
mantissa_start = first_one + 1
|
|
63
|
+
mantissa_bits = binary[mantissa_start:mantissa_start + 52]
|
|
64
|
+
|
|
65
|
+
mantissa_bits = mantissa_bits + ['0'] * (52 - len(mantissa_bits))
|
|
66
|
+
mantissa_bits = ''.join(mantissa_bits[:52])
|
|
67
|
+
|
|
68
|
+
result = sign + exp_bits + mantissa_bits
|
|
69
|
+
|
|
70
|
+
return result
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def unpack_float(byte_data):
|
|
74
|
+
"""从 8 字节解析 IEEE 754 双精度浮点数(不用 struct)"""
|
|
75
|
+
# 修复:如果传入的是字符串,先转换为字节
|
|
76
|
+
if isinstance(byte_data, str):
|
|
77
|
+
# 将二进制字符串转换为字节
|
|
78
|
+
if len(byte_data) == 64:
|
|
79
|
+
# 每 8 位转一个字节
|
|
80
|
+
byte_list = []
|
|
81
|
+
for i in range(0, 64, 8):
|
|
82
|
+
byte_list.append(int(byte_data[i:i+8], 2))
|
|
83
|
+
byte_data = bytes(byte_list)
|
|
84
|
+
else:
|
|
85
|
+
raise ValueError("二进制字符串长度必须是64")
|
|
86
|
+
|
|
87
|
+
# 1. 转成 64 位二进制字符串
|
|
88
|
+
bits = ''.join(format(b, '08b') for b in byte_data)
|
|
89
|
+
|
|
90
|
+
# 2. 解析符号、指数、尾数
|
|
91
|
+
sign = int(bits[0])
|
|
92
|
+
exponent = int(bits[1:12], 2)
|
|
93
|
+
mantissa = int(bits[12:], 2)
|
|
94
|
+
|
|
95
|
+
# 3. 特殊值
|
|
96
|
+
if exponent == 2047:
|
|
97
|
+
if mantissa == 0:
|
|
98
|
+
return float('-inf') if sign else float('inf')
|
|
99
|
+
return float('nan')
|
|
100
|
+
|
|
101
|
+
# 4. 零
|
|
102
|
+
if exponent == 0 and mantissa == 0:
|
|
103
|
+
return -0.0 if sign else 0.0
|
|
104
|
+
|
|
105
|
+
# 5. 非规格化数
|
|
106
|
+
if exponent == 0:
|
|
107
|
+
value = (mantissa / (2**52)) * (2 ** -1022)
|
|
108
|
+
return -value if sign else value
|
|
109
|
+
|
|
110
|
+
# 6. 规格化数
|
|
111
|
+
value = (1 + mantissa / (2**52)) * (2 ** (exponent - 1023))
|
|
112
|
+
return -value if sign else value
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def pack_int(n, bits=None):
|
|
116
|
+
if bits is None:
|
|
117
|
+
# 自动计算所需位数(包括符号位)
|
|
118
|
+
if n >= 0:
|
|
119
|
+
bits = max(1, n.bit_length() + 1)
|
|
120
|
+
else:
|
|
121
|
+
bits = max(1, (-n).bit_length() + 1)
|
|
122
|
+
|
|
123
|
+
if n < 0:
|
|
124
|
+
n = (1 << bits) + n
|
|
125
|
+
|
|
126
|
+
return bin(n)[2:].zfill(bits)[-bits:]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def pack_str(n):
|
|
130
|
+
from .smath import ceil
|
|
131
|
+
result = bin(int.from_bytes(n.encode(), "big"))[2:]
|
|
132
|
+
return result.rjust(ceil(len(result) / 8) * 8, "0")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def bits_to_bytes(bits):
|
|
136
|
+
"""二进制字符串 → bytes"""
|
|
137
|
+
# 补齐到 8 的倍数
|
|
138
|
+
if len(bits) % 8 != 0:
|
|
139
|
+
bits = bits.rjust((len(bits) + 7) // 8 * 8, "0")
|
|
140
|
+
|
|
141
|
+
# 每 8 位转一个字节
|
|
142
|
+
result = []
|
|
143
|
+
for i in range(0, len(bits), 8):
|
|
144
|
+
result.append(int(bits[i:i+8], 2))
|
|
145
|
+
return bytes(result)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def bytes_to_bits(data):
|
|
149
|
+
"""bytes → 二进制字符串"""
|
|
150
|
+
return ''.join(format(b, '08b') for b in data)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def pack_to_file(obj, filename):
|
|
154
|
+
"""打包对象并写入文件"""
|
|
155
|
+
# 1. 打包对象
|
|
156
|
+
packed = pack(obj) # 返回 b'类型标记 + 二进制字符串 + \x00'
|
|
157
|
+
|
|
158
|
+
# 2. 写入文件
|
|
159
|
+
with open(filename, 'wb') as f:
|
|
160
|
+
f.write(packed)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ============ 辅助函数 ============
|
|
164
|
+
|
|
165
|
+
def pack_varint(n):
|
|
166
|
+
"""变长整数编码(无符号)"""
|
|
167
|
+
if n == 0:
|
|
168
|
+
return b'\x00'
|
|
169
|
+
result = []
|
|
170
|
+
while n > 0:
|
|
171
|
+
byte = n & 0x7F
|
|
172
|
+
n >>= 7
|
|
173
|
+
if n > 0:
|
|
174
|
+
byte |= 0x80
|
|
175
|
+
result.append(byte)
|
|
176
|
+
return bytes(result)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def unpack_varint(data, pos):
|
|
180
|
+
"""解包变长整数"""
|
|
181
|
+
result = 0
|
|
182
|
+
shift = 0
|
|
183
|
+
while pos < len(data):
|
|
184
|
+
byte = data[pos]
|
|
185
|
+
pos += 1
|
|
186
|
+
result |= (byte & 0x7F) << shift
|
|
187
|
+
if not (byte & 0x80):
|
|
188
|
+
return result, pos
|
|
189
|
+
shift += 7
|
|
190
|
+
raise ValueError("不完整的变长整数")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
# ============ pack ============
|
|
194
|
+
|
|
195
|
+
def pack(obj):
|
|
196
|
+
"""打包任意对象为二进制"""
|
|
197
|
+
if isinstance(obj, str):
|
|
198
|
+
# 字符串:S + 变长长度 + UTF-8 数据
|
|
199
|
+
data = obj.encode('utf-8')
|
|
200
|
+
return b'S' + pack_varint(len(data)) + data
|
|
201
|
+
|
|
202
|
+
elif isinstance(obj, int):
|
|
203
|
+
# 整数:I + 变长编码
|
|
204
|
+
if obj >= 0:
|
|
205
|
+
return b'I' + pack_varint(obj)
|
|
206
|
+
else:
|
|
207
|
+
# 负数用补码
|
|
208
|
+
return b'i' + pack_varint(-obj)
|
|
209
|
+
|
|
210
|
+
elif isinstance(obj, float):
|
|
211
|
+
# 浮点数:F + 8字节 IEEE 754
|
|
212
|
+
bits = pack_float(obj) # 您的函数,返回二进制字符串
|
|
213
|
+
return b'F' + bits_to_bytes(bits)
|
|
214
|
+
|
|
215
|
+
elif isinstance(obj, bool):
|
|
216
|
+
# 布尔值:B + 1字节 (0 或 1)
|
|
217
|
+
return b'B' + (b'\x01' if obj else b'\x00')
|
|
218
|
+
|
|
219
|
+
elif isinstance(obj, (list, tuple)):
|
|
220
|
+
# 列表:L + 每个元素递归 + E
|
|
221
|
+
result = b'L'
|
|
222
|
+
for item in obj:
|
|
223
|
+
result += pack(item)
|
|
224
|
+
return result + b'E'
|
|
225
|
+
|
|
226
|
+
elif isinstance(obj, dict):
|
|
227
|
+
# 字典:D + 每个键值对递归 + E
|
|
228
|
+
result = b'D'
|
|
229
|
+
for key, value in obj.items():
|
|
230
|
+
result += pack(key) + pack(value)
|
|
231
|
+
return result + b'E'
|
|
232
|
+
|
|
233
|
+
elif obj is None:
|
|
234
|
+
# None:N
|
|
235
|
+
return b'N'
|
|
236
|
+
|
|
237
|
+
else:
|
|
238
|
+
raise TypeError(f"不支持的类型: {type(obj)}")
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
# ============ unpack ============
|
|
242
|
+
|
|
243
|
+
def unpack(data, pos=0):
|
|
244
|
+
"""从二进制解包"""
|
|
245
|
+
if pos >= len(data):
|
|
246
|
+
return None, pos
|
|
247
|
+
|
|
248
|
+
type_code = data[pos:pos+1]
|
|
249
|
+
pos += 1
|
|
250
|
+
|
|
251
|
+
if type_code == b'S':
|
|
252
|
+
# 字符串
|
|
253
|
+
length, pos = unpack_varint(data, pos)
|
|
254
|
+
value = data[pos:pos+length].decode('utf-8')
|
|
255
|
+
return value, pos + length
|
|
256
|
+
|
|
257
|
+
elif type_code == b'I':
|
|
258
|
+
# 无符号整数
|
|
259
|
+
value, pos = unpack_varint(data, pos)
|
|
260
|
+
return value, pos
|
|
261
|
+
|
|
262
|
+
elif type_code == b'i':
|
|
263
|
+
# 负整数
|
|
264
|
+
value, pos = unpack_varint(data, pos)
|
|
265
|
+
return -value, pos
|
|
266
|
+
|
|
267
|
+
elif type_code == b'F':
|
|
268
|
+
# 浮点数 - 直接传入字节数据
|
|
269
|
+
value = unpack_float(data[pos:pos+8])
|
|
270
|
+
return value, pos + 8
|
|
271
|
+
|
|
272
|
+
elif type_code == b'B':
|
|
273
|
+
# 布尔值
|
|
274
|
+
value = data[pos] == 1
|
|
275
|
+
return value, pos + 1
|
|
276
|
+
|
|
277
|
+
elif type_code == b'L':
|
|
278
|
+
# 列表
|
|
279
|
+
result = []
|
|
280
|
+
while pos < len(data):
|
|
281
|
+
if data[pos:pos+1] == b'E':
|
|
282
|
+
pos += 1
|
|
283
|
+
break
|
|
284
|
+
item, pos = unpack(data, pos)
|
|
285
|
+
result.append(item)
|
|
286
|
+
return result, pos
|
|
287
|
+
|
|
288
|
+
elif type_code == b'D':
|
|
289
|
+
# 字典
|
|
290
|
+
result = {}
|
|
291
|
+
while pos < len(data):
|
|
292
|
+
if data[pos:pos+1] == b'E':
|
|
293
|
+
pos += 1
|
|
294
|
+
break
|
|
295
|
+
key, pos = unpack(data, pos)
|
|
296
|
+
value, pos = unpack(data, pos)
|
|
297
|
+
result[key] = value
|
|
298
|
+
return result, pos
|
|
299
|
+
|
|
300
|
+
elif type_code == b'N':
|
|
301
|
+
# None
|
|
302
|
+
return None, pos
|
|
303
|
+
|
|
304
|
+
else:
|
|
305
|
+
raise TypeError(f"未知类型码: {type_code}")
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def dump(obj, filename):
|
|
309
|
+
"""保存对象到文件"""
|
|
310
|
+
with open(filename, 'wb') as f:
|
|
311
|
+
f.write(pack(obj))
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
def load(filename):
|
|
315
|
+
"""从文件加载对象"""
|
|
316
|
+
with open(filename, 'rb') as f:
|
|
317
|
+
data = f.read()
|
|
318
|
+
return unpack(data)[0]
|