timestamp-store 1.0.0__tar.gz
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.
- timestamp_store-1.0.0/LICENSE +21 -0
- timestamp_store-1.0.0/MANIFEST.in +11 -0
- timestamp_store-1.0.0/PKG-INFO +93 -0
- timestamp_store-1.0.0/README.md +60 -0
- timestamp_store-1.0.0/pyproject.toml +42 -0
- timestamp_store-1.0.0/setup.cfg +4 -0
- timestamp_store-1.0.0/setup.py +360 -0
- timestamp_store-1.0.0/timestamp_store/__init__.py +18 -0
- timestamp_store-1.0.0/timestamp_store/src/timestamp_store.cpp +203 -0
- timestamp_store-1.0.0/timestamp_store/timestamp_store.dll +0 -0
- timestamp_store-1.0.0/timestamp_store/wrapper.py +175 -0
- timestamp_store-1.0.0/timestamp_store.egg-info/PKG-INFO +93 -0
- timestamp_store-1.0.0/timestamp_store.egg-info/SOURCES.txt +13 -0
- timestamp_store-1.0.0/timestamp_store.egg-info/dependency_links.txt +1 -0
- timestamp_store-1.0.0/timestamp_store.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shutkanos
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
include README.md
|
|
2
|
+
include LICENSE
|
|
3
|
+
include pyproject.toml
|
|
4
|
+
|
|
5
|
+
recursive-include timestamp_store/src *.cpp *.h *.hpp
|
|
6
|
+
recursive-include timestamp_store *.so *.dylib *.dll
|
|
7
|
+
|
|
8
|
+
global-exclude __pycache__
|
|
9
|
+
global-exclude *.py[cod]
|
|
10
|
+
global-exclude *.so.*
|
|
11
|
+
global-exclude .git*
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: timestamp-store
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Fast timestamp-based data structure with O(log N) operations
|
|
5
|
+
Home-page: https://github.com/shutkanos/timestamp_store
|
|
6
|
+
Author: Shutkanos
|
|
7
|
+
Author-email: Shutkanos <Shutkanos836926@mail.ru>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/shutkanos/timestamp_store
|
|
10
|
+
Project-URL: Repository, https://github.com/shutkanos/timestamp_store
|
|
11
|
+
Keywords: timestamp,data-structure,ctypes
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: C++
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Requires-Python: >=3.7
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: home-page
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
|
|
34
|
+
# TimestampStore
|
|
35
|
+
|
|
36
|
+
Fast data structure for (id, timestamp) pairs with O(log N) operations.
|
|
37
|
+
Warning! Created by claude-opus-4.5 without human intervention.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install git+https://github.com/shutkanos/timestamp_store.git
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Requirements:** C++ compiler (g++, clang++, or MSVC)
|
|
46
|
+
|
|
47
|
+
### Installing compiler
|
|
48
|
+
|
|
49
|
+
- **Ubuntu/Debian:** `sudo apt install g++`
|
|
50
|
+
- **macOS:** `xcode-select --install`
|
|
51
|
+
- **Windows:** Install [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) or:
|
|
52
|
+
|
|
53
|
+
PowerShell:
|
|
54
|
+
```PowerShell
|
|
55
|
+
winget install -e --id MSYS2.MSYS2
|
|
56
|
+
```
|
|
57
|
+
MSYS2:
|
|
58
|
+
```
|
|
59
|
+
pacman -S mingw-w64-x86_64-gcc
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from timestamp_store import TimestampStore
|
|
66
|
+
|
|
67
|
+
store = TimestampStore()
|
|
68
|
+
|
|
69
|
+
# Add pairs
|
|
70
|
+
store.add(1, 100)
|
|
71
|
+
store.add(2, 50)
|
|
72
|
+
store.add(3, 150)
|
|
73
|
+
|
|
74
|
+
# Remove all with timestamp < 120
|
|
75
|
+
removed = store.remove_timestamp(120)
|
|
76
|
+
print(removed) # [2, 1]
|
|
77
|
+
|
|
78
|
+
# Remove by id
|
|
79
|
+
store.remove(3)
|
|
80
|
+
|
|
81
|
+
# Create from list
|
|
82
|
+
store = TimestampStore([(1, 100), (2, 200)])
|
|
83
|
+
# Create from dict
|
|
84
|
+
store = TimestampStore({1: 100, 2: 200})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Complexity
|
|
88
|
+
|
|
89
|
+
| Operation | Complexity |
|
|
90
|
+
|-----------|------------|
|
|
91
|
+
| `add(id, timestamp)` | O(log N) |
|
|
92
|
+
| `remove(id)` | O(log N) |
|
|
93
|
+
| `remove_timestamp(ts)` | O(K) where K = removed count |
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# TimestampStore
|
|
2
|
+
|
|
3
|
+
Fast data structure for (id, timestamp) pairs with O(log N) operations.
|
|
4
|
+
Warning! Created by claude-opus-4.5 without human intervention.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install git+https://github.com/shutkanos/timestamp_store.git
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Requirements:** C++ compiler (g++, clang++, or MSVC)
|
|
13
|
+
|
|
14
|
+
### Installing compiler
|
|
15
|
+
|
|
16
|
+
- **Ubuntu/Debian:** `sudo apt install g++`
|
|
17
|
+
- **macOS:** `xcode-select --install`
|
|
18
|
+
- **Windows:** Install [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) or:
|
|
19
|
+
|
|
20
|
+
PowerShell:
|
|
21
|
+
```PowerShell
|
|
22
|
+
winget install -e --id MSYS2.MSYS2
|
|
23
|
+
```
|
|
24
|
+
MSYS2:
|
|
25
|
+
```
|
|
26
|
+
pacman -S mingw-w64-x86_64-gcc
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from timestamp_store import TimestampStore
|
|
33
|
+
|
|
34
|
+
store = TimestampStore()
|
|
35
|
+
|
|
36
|
+
# Add pairs
|
|
37
|
+
store.add(1, 100)
|
|
38
|
+
store.add(2, 50)
|
|
39
|
+
store.add(3, 150)
|
|
40
|
+
|
|
41
|
+
# Remove all with timestamp < 120
|
|
42
|
+
removed = store.remove_timestamp(120)
|
|
43
|
+
print(removed) # [2, 1]
|
|
44
|
+
|
|
45
|
+
# Remove by id
|
|
46
|
+
store.remove(3)
|
|
47
|
+
|
|
48
|
+
# Create from list
|
|
49
|
+
store = TimestampStore([(1, 100), (2, 200)])
|
|
50
|
+
# Create from dict
|
|
51
|
+
store = TimestampStore({1: 100, 2: 200})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Complexity
|
|
55
|
+
|
|
56
|
+
| Operation | Complexity |
|
|
57
|
+
|-----------|------------|
|
|
58
|
+
| `add(id, timestamp)` | O(log N) |
|
|
59
|
+
| `remove(id)` | O(log N) |
|
|
60
|
+
| `remove_timestamp(ts)` | O(K) where K = removed count |
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=45", "wheel", "setuptools-scm>=6.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "timestamp-store"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Fast timestamp-based data structure with O(log N) operations"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
requires-python = ">=3.7"
|
|
12
|
+
authors = [
|
|
13
|
+
{name = "Shutkanos", email = "Shutkanos836926@mail.ru"}
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.7",
|
|
21
|
+
"Programming Language :: Python :: 3.8",
|
|
22
|
+
"Programming Language :: Python :: 3.9",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Programming Language :: C++",
|
|
29
|
+
"Operating System :: OS Independent",
|
|
30
|
+
]
|
|
31
|
+
keywords = ["timestamp", "data-structure", "ctypes"]
|
|
32
|
+
|
|
33
|
+
[project.urls]
|
|
34
|
+
Homepage = "https://github.com/shutkanos/timestamp_store"
|
|
35
|
+
Repository = "https://github.com/shutkanos/timestamp_store"
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["."]
|
|
39
|
+
include = ["timestamp_store*"]
|
|
40
|
+
|
|
41
|
+
[tool.setuptools.package-data]
|
|
42
|
+
timestamp_store = ["src/*.cpp", "*.so", "*.dylib", "*.dll"]
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import platform
|
|
4
|
+
import subprocess
|
|
5
|
+
import shutil
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from setuptools import setup, find_packages
|
|
9
|
+
from setuptools.command.build_py import build_py
|
|
10
|
+
from setuptools.command.develop import develop
|
|
11
|
+
from setuptools.command.install import install
|
|
12
|
+
from setuptools.command.egg_info import egg_info
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_library_name():
|
|
16
|
+
system = platform.system()
|
|
17
|
+
if system == "Windows":
|
|
18
|
+
return "timestamp_store.dll"
|
|
19
|
+
elif system == "Darwin":
|
|
20
|
+
return "libtimestamp_store.dylib"
|
|
21
|
+
else:
|
|
22
|
+
return "libtimestamp_store.so"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def find_mingw_path():
|
|
26
|
+
mingw_search_paths = [
|
|
27
|
+
Path("C:/msys64/mingw64/bin"),
|
|
28
|
+
Path("C:/msys64/ucrt64/bin"),
|
|
29
|
+
Path("C:/msys64/clang64/bin"),
|
|
30
|
+
Path("C:/msys64/mingw32/bin"),
|
|
31
|
+
Path("C:/msys2/mingw64/bin"),
|
|
32
|
+
Path("C:/msys2/ucrt64/bin"),
|
|
33
|
+
Path("C:/mingw64/bin"),
|
|
34
|
+
Path("C:/mingw/bin"),
|
|
35
|
+
Path("C:/MinGW/bin"),
|
|
36
|
+
Path("C:/tools/mingw64/bin"),
|
|
37
|
+
Path(os.environ.get("USERPROFILE", "")) / "scoop/apps/mingw/current/bin",
|
|
38
|
+
Path(os.environ.get("ProgramFiles", "C:\\Program Files")) / "mingw-w64/x86_64-8.1.0-posix-seh-rt_v6-rev0/mingw64/bin",
|
|
39
|
+
Path(os.environ.get("ProgramFiles", "C:\\Program Files")) / "mingw64/bin",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
for env_var in ["MINGW_HOME", "MINGW64_HOME", "MSYS2_HOME"]:
|
|
43
|
+
if env_var in os.environ:
|
|
44
|
+
env_path = Path(os.environ[env_var])
|
|
45
|
+
if (env_path / "bin" / "g++.exe").exists():
|
|
46
|
+
mingw_search_paths.insert(0, env_path / "bin")
|
|
47
|
+
elif (env_path / "g++.exe").exists():
|
|
48
|
+
mingw_search_paths.insert(0, env_path)
|
|
49
|
+
|
|
50
|
+
for path in mingw_search_paths:
|
|
51
|
+
if path.exists() and (path / "g++.exe").exists():
|
|
52
|
+
print(f"Found MinGW at: {path}")
|
|
53
|
+
return path
|
|
54
|
+
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def find_msvc_vcvarsall():
|
|
59
|
+
program_files_x86 = os.environ.get("ProgramFiles(x86)", "C:\\Program Files (x86)")
|
|
60
|
+
program_files = os.environ.get("ProgramFiles", "C:\\Program Files")
|
|
61
|
+
|
|
62
|
+
vswhere_paths = [
|
|
63
|
+
Path(program_files_x86) / "Microsoft Visual Studio/Installer/vswhere.exe",
|
|
64
|
+
Path(program_files) / "Microsoft Visual Studio/Installer/vswhere.exe",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
vswhere = None
|
|
68
|
+
for p in vswhere_paths:
|
|
69
|
+
if p.exists():
|
|
70
|
+
vswhere = p
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
if vswhere:
|
|
74
|
+
try:
|
|
75
|
+
result = subprocess.run(
|
|
76
|
+
[
|
|
77
|
+
str(vswhere),
|
|
78
|
+
"-latest",
|
|
79
|
+
"-property", "installationPath",
|
|
80
|
+
"-requires", "Microsoft.VisualStudio.Component.VC.Tools.x86.x64"
|
|
81
|
+
],
|
|
82
|
+
capture_output=True,
|
|
83
|
+
text=True,
|
|
84
|
+
check=True
|
|
85
|
+
)
|
|
86
|
+
vs_path = Path(result.stdout.strip())
|
|
87
|
+
vcvarsall = vs_path / "VC/Auxiliary/Build/vcvarsall.bat"
|
|
88
|
+
if vcvarsall.exists():
|
|
89
|
+
print(f"Found MSVC vcvarsall at: {vcvarsall}")
|
|
90
|
+
return vcvarsall
|
|
91
|
+
except subprocess.CalledProcessError:
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
vs_years = ["2022", "2019", "2017"]
|
|
95
|
+
vs_editions = ["Enterprise", "Professional", "Community", "BuildTools"]
|
|
96
|
+
|
|
97
|
+
for year in vs_years:
|
|
98
|
+
for edition in vs_editions:
|
|
99
|
+
for pf in [program_files, program_files_x86]:
|
|
100
|
+
vcvarsall = Path(pf) / f"Microsoft Visual Studio/{year}/{edition}/VC/Auxiliary/Build/vcvarsall.bat"
|
|
101
|
+
if vcvarsall.exists():
|
|
102
|
+
print(f"Found MSVC vcvarsall at: {vcvarsall}")
|
|
103
|
+
return vcvarsall
|
|
104
|
+
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_msvc_environment(vcvarsall):
|
|
109
|
+
arch = "x64" if platform.machine().endswith('64') else "x86"
|
|
110
|
+
|
|
111
|
+
cmd = f'"{vcvarsall}" {arch} >nul 2>&1 && set'
|
|
112
|
+
|
|
113
|
+
try:
|
|
114
|
+
result = subprocess.run(
|
|
115
|
+
cmd,
|
|
116
|
+
shell=True,
|
|
117
|
+
capture_output=True,
|
|
118
|
+
text=True,
|
|
119
|
+
check=True
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
env = {}
|
|
123
|
+
for line in result.stdout.splitlines():
|
|
124
|
+
if '=' in line:
|
|
125
|
+
key, _, value = line.partition('=')
|
|
126
|
+
env[key] = value
|
|
127
|
+
|
|
128
|
+
return env
|
|
129
|
+
except subprocess.CalledProcessError as e:
|
|
130
|
+
print(f"Warning: Failed to setup MSVC environment: {e}")
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_compiler_command():
|
|
135
|
+
system = platform.system()
|
|
136
|
+
lib_name = get_library_name()
|
|
137
|
+
|
|
138
|
+
src_dir = Path(__file__).parent / "timestamp_store" / "src"
|
|
139
|
+
cpp_file = src_dir / "timestamp_store.cpp"
|
|
140
|
+
output_dir = Path(__file__).parent / "timestamp_store"
|
|
141
|
+
output_file = output_dir / lib_name
|
|
142
|
+
|
|
143
|
+
env = os.environ.copy()
|
|
144
|
+
|
|
145
|
+
if system == "Windows":
|
|
146
|
+
mingw_path = find_mingw_path()
|
|
147
|
+
if mingw_path:
|
|
148
|
+
env["PATH"] = str(mingw_path) + os.pathsep + env.get("PATH", "")
|
|
149
|
+
|
|
150
|
+
gpp_path = mingw_path / "g++.exe"
|
|
151
|
+
|
|
152
|
+
return [
|
|
153
|
+
str(gpp_path),
|
|
154
|
+
"-O3", "-std=c++17", "-shared",
|
|
155
|
+
"-static-libgcc",
|
|
156
|
+
"-static-libstdc++",
|
|
157
|
+
"-static",
|
|
158
|
+
"-o", str(output_file),
|
|
159
|
+
str(cpp_file)
|
|
160
|
+
], env
|
|
161
|
+
|
|
162
|
+
vcvarsall = find_msvc_vcvarsall()
|
|
163
|
+
if vcvarsall:
|
|
164
|
+
msvc_env = get_msvc_environment(vcvarsall)
|
|
165
|
+
if msvc_env:
|
|
166
|
+
return [
|
|
167
|
+
"cl",
|
|
168
|
+
"/O2",
|
|
169
|
+
"/LD",
|
|
170
|
+
"/EHsc",
|
|
171
|
+
"/std:c++17",
|
|
172
|
+
"/MT",
|
|
173
|
+
str(cpp_file),
|
|
174
|
+
f"/Fe:{output_file}",
|
|
175
|
+
f"/Fo:{output_dir}\\",
|
|
176
|
+
], msvc_env
|
|
177
|
+
|
|
178
|
+
if shutil.which("g++"):
|
|
179
|
+
print("Using g++ from PATH")
|
|
180
|
+
return [
|
|
181
|
+
"g++", "-O3", "-std=c++17", "-shared",
|
|
182
|
+
"-static-libgcc", "-static-libstdc++",
|
|
183
|
+
"-o", str(output_file),
|
|
184
|
+
str(cpp_file)
|
|
185
|
+
], env
|
|
186
|
+
|
|
187
|
+
if shutil.which("cl"):
|
|
188
|
+
print("Using cl from PATH")
|
|
189
|
+
return [
|
|
190
|
+
"cl", "/O2", "/LD", "/EHsc",
|
|
191
|
+
"/std:c++17", "/MT",
|
|
192
|
+
str(cpp_file),
|
|
193
|
+
f"/Fe:{output_file}"
|
|
194
|
+
], env
|
|
195
|
+
raise RuntimeError(
|
|
196
|
+
"No C++ compiler found on Windows!\n\n"
|
|
197
|
+
"Please install one of the following:\n"
|
|
198
|
+
"1. MSYS2 MinGW: https://www.msys2.org/\n"
|
|
199
|
+
" Then run: pacman -S mingw-w64-x86_64-gcc\n\n"
|
|
200
|
+
"2. Visual Studio Build Tools: https://visualstudio.microsoft.com/visual-cpp-build-tools/\n"
|
|
201
|
+
" Select 'Desktop development with C++'\n\n"
|
|
202
|
+
"3. Standalone MinGW-w64: https://www.mingw-w64.org/downloads/"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
elif system == "Darwin":
|
|
206
|
+
compiler = "clang++" if shutil.which("clang++") else "g++"
|
|
207
|
+
return [
|
|
208
|
+
compiler, "-O3", "-std=c++17",
|
|
209
|
+
"-shared", "-fPIC",
|
|
210
|
+
"-o", str(output_file),
|
|
211
|
+
str(cpp_file)
|
|
212
|
+
], env
|
|
213
|
+
|
|
214
|
+
else:
|
|
215
|
+
if not shutil.which("g++"):
|
|
216
|
+
raise RuntimeError(
|
|
217
|
+
"g++ not found. Please install:\n"
|
|
218
|
+
"Ubuntu/Debian: sudo apt install g++\n"
|
|
219
|
+
"Fedora: sudo dnf install gcc-c++\n"
|
|
220
|
+
"Arch: sudo pacman -S gcc"
|
|
221
|
+
)
|
|
222
|
+
return [
|
|
223
|
+
"g++", "-O3", "-std=c++17",
|
|
224
|
+
"-shared", "-fPIC",
|
|
225
|
+
"-o", str(output_file),
|
|
226
|
+
str(cpp_file)
|
|
227
|
+
], env
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def compile_cpp_library():
|
|
231
|
+
lib_name = get_library_name()
|
|
232
|
+
output_dir = Path(__file__).parent / "timestamp_store"
|
|
233
|
+
output_file = output_dir / lib_name
|
|
234
|
+
|
|
235
|
+
if output_file.exists():
|
|
236
|
+
print(f"Library {lib_name} already exists, skipping compilation")
|
|
237
|
+
return
|
|
238
|
+
|
|
239
|
+
print(f"Compiling C++ library: {lib_name}")
|
|
240
|
+
print(f"Platform: {platform.system()} {platform.machine()}")
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
cmd, env = get_compiler_command()
|
|
244
|
+
print(f"Running: {' '.join(cmd)}")
|
|
245
|
+
|
|
246
|
+
path_preview = env.get("PATH", "").split(os.pathsep)[:3]
|
|
247
|
+
print(f"PATH preview: {path_preview}")
|
|
248
|
+
|
|
249
|
+
result = subprocess.run(
|
|
250
|
+
cmd,
|
|
251
|
+
check=True,
|
|
252
|
+
capture_output=True,
|
|
253
|
+
text=True,
|
|
254
|
+
env=env,
|
|
255
|
+
cwd=str(output_dir)
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
if result.stdout:
|
|
259
|
+
print(result.stdout)
|
|
260
|
+
|
|
261
|
+
if output_file.exists():
|
|
262
|
+
print(f"Successfully compiled {lib_name}")
|
|
263
|
+
print(f"Library size: {output_file.stat().st_size} bytes")
|
|
264
|
+
else:
|
|
265
|
+
raise RuntimeError(f"Compilation succeeded but {lib_name} not found")
|
|
266
|
+
|
|
267
|
+
except subprocess.CalledProcessError as e:
|
|
268
|
+
print(f"Compilation failed!")
|
|
269
|
+
print(f"Command: {' '.join(cmd)}")
|
|
270
|
+
print(f"stdout: {e.stdout}")
|
|
271
|
+
print(f"stderr: {e.stderr}")
|
|
272
|
+
raise RuntimeError(f"Failed to compile C++ library: {e}")
|
|
273
|
+
except FileNotFoundError as e:
|
|
274
|
+
raise RuntimeError(
|
|
275
|
+
f"C++ compiler not found: {e}\n"
|
|
276
|
+
f"Please install g++, clang++, or MSVC."
|
|
277
|
+
)
|
|
278
|
+
finally:
|
|
279
|
+
if platform.system() == "Windows":
|
|
280
|
+
for ext in [".obj", ".exp", ".lib"]:
|
|
281
|
+
for f in output_dir.glob(f"*{ext}"):
|
|
282
|
+
try:
|
|
283
|
+
f.unlink()
|
|
284
|
+
except:
|
|
285
|
+
pass
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
class BuildPyWithCompile(build_py):
|
|
289
|
+
def run(self):
|
|
290
|
+
compile_cpp_library()
|
|
291
|
+
super().run()
|
|
292
|
+
|
|
293
|
+
lib_name = get_library_name()
|
|
294
|
+
src_lib = Path(__file__).parent / "timestamp_store" / lib_name
|
|
295
|
+
|
|
296
|
+
if src_lib.exists():
|
|
297
|
+
dest_dir = Path(self.build_lib) / "timestamp_store"
|
|
298
|
+
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
299
|
+
dest_lib = dest_dir / lib_name
|
|
300
|
+
shutil.copy2(src_lib, dest_lib)
|
|
301
|
+
print(f"Copied {lib_name} to {dest_lib}")
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class DevelopWithCompile(develop):
|
|
305
|
+
def run(self):
|
|
306
|
+
compile_cpp_library()
|
|
307
|
+
super().run()
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class InstallWithCompile(install):
|
|
311
|
+
def run(self):
|
|
312
|
+
compile_cpp_library()
|
|
313
|
+
super().run()
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
class EggInfoWithCompile(egg_info):
|
|
317
|
+
def run(self):
|
|
318
|
+
compile_cpp_library()
|
|
319
|
+
super().run()
|
|
320
|
+
|
|
321
|
+
long_description = ""
|
|
322
|
+
if os.path.exists("README.md"):
|
|
323
|
+
with open("README.md", encoding="utf-8") as f:
|
|
324
|
+
long_description = f.read()
|
|
325
|
+
|
|
326
|
+
setup(
|
|
327
|
+
name="timestamp-store",
|
|
328
|
+
version="1.0.0",
|
|
329
|
+
description="Fast timestamp-based data structure with O(log N) operations",
|
|
330
|
+
long_description=long_description,
|
|
331
|
+
long_description_content_type="text/markdown",
|
|
332
|
+
author='Shutkanos',
|
|
333
|
+
author_email='Shutkanos836926@mail.ru',
|
|
334
|
+
url="https://github.com/shutkanos/timestamp_store",
|
|
335
|
+
packages=find_packages(),
|
|
336
|
+
package_data={
|
|
337
|
+
"timestamp_store": [
|
|
338
|
+
"src/*.cpp",
|
|
339
|
+
"*.so",
|
|
340
|
+
"*.dylib",
|
|
341
|
+
"*.dll",
|
|
342
|
+
],
|
|
343
|
+
},
|
|
344
|
+
include_package_data=True,
|
|
345
|
+
python_requires=">=3.7",
|
|
346
|
+
classifiers=[
|
|
347
|
+
"Development Status :: 4 - Beta",
|
|
348
|
+
"Intended Audience :: Developers",
|
|
349
|
+
"License :: OSI Approved :: MIT License",
|
|
350
|
+
"Programming Language :: Python :: 3",
|
|
351
|
+
"Programming Language :: C++",
|
|
352
|
+
"Operating System :: OS Independent",
|
|
353
|
+
],
|
|
354
|
+
cmdclass={
|
|
355
|
+
"build_py": BuildPyWithCompile,
|
|
356
|
+
"develop": DevelopWithCompile,
|
|
357
|
+
"install": InstallWithCompile,
|
|
358
|
+
"egg_info": EggInfoWithCompile,
|
|
359
|
+
},
|
|
360
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TimestampStore - fast data structure for pairs (id, timestamp)
|
|
3
|
+
|
|
4
|
+
Usage example:
|
|
5
|
+
from timestamp_store import TimestampStore
|
|
6
|
+
|
|
7
|
+
store = TimestampStore()
|
|
8
|
+
store.add(1, 100)
|
|
9
|
+
store.add(2, 50)
|
|
10
|
+
|
|
11
|
+
removed = store.remove_timestamp(80) # [2]
|
|
12
|
+
removed = store.remove_timestamp(120) # [1]
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from .wrapper import TimestampStore
|
|
16
|
+
|
|
17
|
+
__version__ = "1.0.0"
|
|
18
|
+
__all__ = ["TimestampStore"]
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#include <map>
|
|
2
|
+
#include <unordered_map>
|
|
3
|
+
#include <unordered_set>
|
|
4
|
+
#include <vector>
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <algorithm>
|
|
7
|
+
|
|
8
|
+
class TimestampStore {
|
|
9
|
+
private:
|
|
10
|
+
std::map<int64_t, std::unordered_set<int64_t>> time_to_ids_;
|
|
11
|
+
|
|
12
|
+
std::unordered_map<int64_t, int64_t> id_to_time_;
|
|
13
|
+
|
|
14
|
+
public:
|
|
15
|
+
TimestampStore() = default;
|
|
16
|
+
|
|
17
|
+
void add(int64_t id, int64_t timestamp) {
|
|
18
|
+
auto it = id_to_time_.find(id);
|
|
19
|
+
|
|
20
|
+
if (it != id_to_time_.end()) {
|
|
21
|
+
int64_t old_time = it->second;
|
|
22
|
+
|
|
23
|
+
if (old_time == timestamp) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
auto old_time_it = time_to_ids_.find(old_time);
|
|
28
|
+
if (old_time_it != time_to_ids_.end()) {
|
|
29
|
+
old_time_it->second.erase(id);
|
|
30
|
+
if (old_time_it->second.empty()) {
|
|
31
|
+
time_to_ids_.erase(old_time_it);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
it->second = timestamp;
|
|
36
|
+
} else {
|
|
37
|
+
id_to_time_[id] = timestamp;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
time_to_ids_[timestamp].insert(id);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
bool remove(int64_t id) {
|
|
44
|
+
auto it = id_to_time_.find(id);
|
|
45
|
+
if (it == id_to_time_.end()) {
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
int64_t timestamp = it->second;
|
|
50
|
+
id_to_time_.erase(it);
|
|
51
|
+
|
|
52
|
+
auto time_it = time_to_ids_.find(timestamp);
|
|
53
|
+
if (time_it != time_to_ids_.end()) {
|
|
54
|
+
time_it->second.erase(id);
|
|
55
|
+
if (time_it->second.empty()) {
|
|
56
|
+
time_to_ids_.erase(time_it);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
std::vector<int64_t> remove_before_timestamp(int64_t timestamp) {
|
|
64
|
+
std::vector<int64_t> removed_ids;
|
|
65
|
+
|
|
66
|
+
while (!time_to_ids_.empty()) {
|
|
67
|
+
auto it = time_to_ids_.begin();
|
|
68
|
+
|
|
69
|
+
if (it->first >= timestamp) {
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
for (int64_t id : it->second) {
|
|
74
|
+
removed_ids.push_back(id);
|
|
75
|
+
id_to_time_.erase(id);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
time_to_ids_.erase(it);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return removed_ids;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
size_t size() const {
|
|
85
|
+
return id_to_time_.size();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
bool empty() const {
|
|
89
|
+
return id_to_time_.empty();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
int64_t get_min_timestamp() const {
|
|
93
|
+
if (time_to_ids_.empty()) {
|
|
94
|
+
return -1;
|
|
95
|
+
}
|
|
96
|
+
return time_to_ids_.begin()->first;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
bool contains(int64_t id) const {
|
|
100
|
+
return id_to_time_.count(id) > 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
int64_t get_timestamp(int64_t id) const {
|
|
104
|
+
auto it = id_to_time_.find(id);
|
|
105
|
+
if (it == id_to_time_.end()) {
|
|
106
|
+
return -1;
|
|
107
|
+
}
|
|
108
|
+
return it->second;
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
// ============================================================================
|
|
114
|
+
// C API ctypes
|
|
115
|
+
// ============================================================================
|
|
116
|
+
|
|
117
|
+
#ifdef _WIN32
|
|
118
|
+
#define EXPORT extern "C" __declspec(dllexport)
|
|
119
|
+
#else
|
|
120
|
+
#define EXPORT extern "C" __attribute__((visibility("default")))
|
|
121
|
+
#endif
|
|
122
|
+
|
|
123
|
+
EXPORT TimestampStore* ts_create() {
|
|
124
|
+
return new TimestampStore();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
EXPORT TimestampStore* ts_create_from_arrays(
|
|
128
|
+
const int64_t* ids,
|
|
129
|
+
const int64_t* timestamps,
|
|
130
|
+
int64_t count
|
|
131
|
+
) {
|
|
132
|
+
TimestampStore* store = new TimestampStore();
|
|
133
|
+
for (int64_t i = 0; i < count; ++i) {
|
|
134
|
+
store->add(ids[i], timestamps[i]);
|
|
135
|
+
}
|
|
136
|
+
return store;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
EXPORT void ts_destroy(TimestampStore* store) {
|
|
140
|
+
delete store;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
EXPORT void ts_add(TimestampStore* store, int64_t id, int64_t timestamp) {
|
|
144
|
+
if (store) {
|
|
145
|
+
store->add(id, timestamp);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
EXPORT int32_t ts_remove(TimestampStore* store, int64_t id) {
|
|
150
|
+
if (!store) return 0;
|
|
151
|
+
return store->remove(id) ? 1 : 0;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
EXPORT int64_t* ts_remove_before_timestamp(
|
|
155
|
+
TimestampStore* store,
|
|
156
|
+
int64_t timestamp,
|
|
157
|
+
int64_t* out_size
|
|
158
|
+
) {
|
|
159
|
+
if (!store || !out_size) {
|
|
160
|
+
if (out_size) *out_size = 0;
|
|
161
|
+
return nullptr;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
std::vector<int64_t> result = store->remove_before_timestamp(timestamp);
|
|
165
|
+
*out_size = static_cast<int64_t>(result.size());
|
|
166
|
+
|
|
167
|
+
if (result.empty()) {
|
|
168
|
+
return nullptr;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
int64_t* arr = new int64_t[result.size()];
|
|
172
|
+
std::copy(result.begin(), result.end(), arr);
|
|
173
|
+
return arr;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
EXPORT void ts_free_array(int64_t* arr) {
|
|
177
|
+
delete[] arr;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
EXPORT int64_t ts_size(TimestampStore* store) {
|
|
181
|
+
if (!store) return 0;
|
|
182
|
+
return static_cast<int64_t>(store->size());
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
EXPORT int32_t ts_empty(TimestampStore* store) {
|
|
186
|
+
if (!store) return 1;
|
|
187
|
+
return store->empty() ? 1 : 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
EXPORT int64_t ts_get_min_timestamp(TimestampStore* store) {
|
|
191
|
+
if (!store) return -1;
|
|
192
|
+
return store->get_min_timestamp();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
EXPORT int32_t ts_contains(TimestampStore* store, int64_t id) {
|
|
196
|
+
if (!store) return 0;
|
|
197
|
+
return store->contains(id) ? 1 : 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
EXPORT int64_t ts_get_timestamp(TimestampStore* store, int64_t id) {
|
|
201
|
+
if (!store) return -1;
|
|
202
|
+
return store->get_timestamp(id);
|
|
203
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import platform
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List, Tuple, Optional, Union, Dict
|
|
5
|
+
|
|
6
|
+
class TimestampStore:
|
|
7
|
+
_lib: ctypes.CDLL = None
|
|
8
|
+
_lib_path: str = None
|
|
9
|
+
|
|
10
|
+
@classmethod
|
|
11
|
+
def _get_lib_name(cls) -> str:
|
|
12
|
+
system = platform.system()
|
|
13
|
+
if system == "Windows":
|
|
14
|
+
return "timestamp_store.dll"
|
|
15
|
+
elif system == "Darwin":
|
|
16
|
+
return "libtimestamp_store.dylib"
|
|
17
|
+
else:
|
|
18
|
+
return "libtimestamp_store.so"
|
|
19
|
+
|
|
20
|
+
@classmethod
|
|
21
|
+
def _find_library(cls) -> str:
|
|
22
|
+
lib_name = cls._get_lib_name()
|
|
23
|
+
|
|
24
|
+
search_paths = [
|
|
25
|
+
Path(__file__).parent / lib_name,
|
|
26
|
+
Path(__file__).parent / "src" / lib_name,
|
|
27
|
+
Path.cwd() / lib_name,
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
for path in search_paths:
|
|
31
|
+
if path.exists():
|
|
32
|
+
return str(path)
|
|
33
|
+
|
|
34
|
+
raise FileNotFoundError(
|
|
35
|
+
f"Could not find {lib_name}. "
|
|
36
|
+
f"Searched in: {[str(p) for p in search_paths]}. "
|
|
37
|
+
f"Try reinstalling the package: pip install --force-reinstall git+https://github.com/shutkanos/timestamp_store.git"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def _load_library(cls, lib_path: Optional[str] = None) -> ctypes.CDLL:
|
|
42
|
+
if lib_path is None:
|
|
43
|
+
lib_path = cls._find_library()
|
|
44
|
+
|
|
45
|
+
if cls._lib is not None and cls._lib_path == lib_path:
|
|
46
|
+
return cls._lib
|
|
47
|
+
|
|
48
|
+
lib = ctypes.CDLL(lib_path)
|
|
49
|
+
|
|
50
|
+
lib.ts_create.restype = ctypes.c_void_p
|
|
51
|
+
lib.ts_create.argtypes = []
|
|
52
|
+
|
|
53
|
+
lib.ts_create_from_arrays.restype = ctypes.c_void_p
|
|
54
|
+
lib.ts_create_from_arrays.argtypes = [
|
|
55
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
56
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
57
|
+
ctypes.c_int64
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
lib.ts_destroy.restype = None
|
|
61
|
+
lib.ts_destroy.argtypes = [ctypes.c_void_p]
|
|
62
|
+
|
|
63
|
+
lib.ts_add.restype = None
|
|
64
|
+
lib.ts_add.argtypes = [ctypes.c_void_p, ctypes.c_int64, ctypes.c_int64]
|
|
65
|
+
|
|
66
|
+
lib.ts_remove.restype = ctypes.c_int32
|
|
67
|
+
lib.ts_remove.argtypes = [ctypes.c_void_p, ctypes.c_int64]
|
|
68
|
+
|
|
69
|
+
lib.ts_remove_before_timestamp.restype = ctypes.POINTER(ctypes.c_int64)
|
|
70
|
+
lib.ts_remove_before_timestamp.argtypes = [
|
|
71
|
+
ctypes.c_void_p,
|
|
72
|
+
ctypes.c_int64,
|
|
73
|
+
ctypes.POINTER(ctypes.c_int64)
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
lib.ts_free_array.restype = None
|
|
77
|
+
lib.ts_free_array.argtypes = [ctypes.POINTER(ctypes.c_int64)]
|
|
78
|
+
|
|
79
|
+
lib.ts_size.restype = ctypes.c_int64
|
|
80
|
+
lib.ts_size.argtypes = [ctypes.c_void_p]
|
|
81
|
+
|
|
82
|
+
lib.ts_empty.restype = ctypes.c_int32
|
|
83
|
+
lib.ts_empty.argtypes = [ctypes.c_void_p]
|
|
84
|
+
|
|
85
|
+
lib.ts_get_min_timestamp.restype = ctypes.c_int64
|
|
86
|
+
lib.ts_get_min_timestamp.argtypes = [ctypes.c_void_p]
|
|
87
|
+
|
|
88
|
+
lib.ts_contains.restype = ctypes.c_int32
|
|
89
|
+
lib.ts_contains.argtypes = [ctypes.c_void_p, ctypes.c_int64]
|
|
90
|
+
|
|
91
|
+
lib.ts_get_timestamp.restype = ctypes.c_int64
|
|
92
|
+
lib.ts_get_timestamp.argtypes = [ctypes.c_void_p, ctypes.c_int64]
|
|
93
|
+
|
|
94
|
+
cls._lib = lib
|
|
95
|
+
cls._lib_path = lib_path
|
|
96
|
+
return lib
|
|
97
|
+
|
|
98
|
+
def __init__(self, data: Optional[Union[List[Tuple[int, int]], Dict[int, int]]] = None, *, lib_path: Optional[str] = None):
|
|
99
|
+
self._lib_instance = self._load_library(lib_path)
|
|
100
|
+
|
|
101
|
+
if data is None:
|
|
102
|
+
self._store = self._lib_instance.ts_create()
|
|
103
|
+
else:
|
|
104
|
+
if isinstance(data, dict):
|
|
105
|
+
pairs = list(data.items())
|
|
106
|
+
else:
|
|
107
|
+
pairs = list(data)
|
|
108
|
+
|
|
109
|
+
if not pairs:
|
|
110
|
+
self._store = self._lib_instance.ts_create()
|
|
111
|
+
else:
|
|
112
|
+
n = len(pairs)
|
|
113
|
+
ids_array = (ctypes.c_int64 * n)()
|
|
114
|
+
timestamps_array = (ctypes.c_int64 * n)()
|
|
115
|
+
|
|
116
|
+
for i, (id_val, ts_val) in enumerate(pairs):
|
|
117
|
+
ids_array[i] = id_val
|
|
118
|
+
timestamps_array[i] = ts_val
|
|
119
|
+
|
|
120
|
+
self._store = self._lib_instance.ts_create_from_arrays(
|
|
121
|
+
ids_array, timestamps_array, n
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
if not self._store:
|
|
125
|
+
raise MemoryError("Failed to create TimestampStore")
|
|
126
|
+
|
|
127
|
+
def __del__(self):
|
|
128
|
+
if hasattr(self, '_store') and self._store and hasattr(self, '_lib_instance'):
|
|
129
|
+
self._lib_instance.ts_destroy(self._store)
|
|
130
|
+
self._store = None
|
|
131
|
+
|
|
132
|
+
def add(self, id: int, timestamp: int) -> None:
|
|
133
|
+
self._lib_instance.ts_add(self._store, id, timestamp)
|
|
134
|
+
|
|
135
|
+
def remove(self, id: int) -> bool:
|
|
136
|
+
return bool(self._lib_instance.ts_remove(self._store, id))
|
|
137
|
+
|
|
138
|
+
def remove_timestamp(self, timestamp: int) -> List[int]:
|
|
139
|
+
"""
|
|
140
|
+
Delete all elements with a timestamp value less than the specified argument
|
|
141
|
+
:return: list of deleted IDs.
|
|
142
|
+
"""
|
|
143
|
+
size = ctypes.c_int64(0)
|
|
144
|
+
arr_ptr = self._lib_instance.ts_remove_before_timestamp(
|
|
145
|
+
self._store,
|
|
146
|
+
timestamp,
|
|
147
|
+
ctypes.byref(size)
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if size.value == 0 or not arr_ptr:
|
|
151
|
+
return []
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
result = [arr_ptr[i] for i in range(size.value)]
|
|
155
|
+
finally:
|
|
156
|
+
self._lib_instance.ts_free_array(arr_ptr)
|
|
157
|
+
|
|
158
|
+
return result
|
|
159
|
+
|
|
160
|
+
def get_timestamp(self, id: int) -> Optional[int]:
|
|
161
|
+
ts = self._lib_instance.ts_get_timestamp(self._store, id)
|
|
162
|
+
return ts if ts >= 0 else None
|
|
163
|
+
|
|
164
|
+
def get_min_timestamp(self) -> Optional[int]:
|
|
165
|
+
ts = self._lib_instance.ts_get_min_timestamp(self._store)
|
|
166
|
+
return ts if ts >= 0 else None
|
|
167
|
+
|
|
168
|
+
def __len__(self) -> int:
|
|
169
|
+
return self._lib_instance.ts_size(self._store)
|
|
170
|
+
|
|
171
|
+
def __bool__(self) -> bool:
|
|
172
|
+
return not self._lib_instance.ts_empty(self._store)
|
|
173
|
+
|
|
174
|
+
def __contains__(self, id: int) -> bool:
|
|
175
|
+
return bool(self._lib_instance.ts_contains(self._store, id))
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: timestamp-store
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Fast timestamp-based data structure with O(log N) operations
|
|
5
|
+
Home-page: https://github.com/shutkanos/timestamp_store
|
|
6
|
+
Author: Shutkanos
|
|
7
|
+
Author-email: Shutkanos <Shutkanos836926@mail.ru>
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Homepage, https://github.com/shutkanos/timestamp_store
|
|
10
|
+
Project-URL: Repository, https://github.com/shutkanos/timestamp_store
|
|
11
|
+
Keywords: timestamp,data-structure,ctypes
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
|
+
Classifier: Programming Language :: C++
|
|
25
|
+
Classifier: Operating System :: OS Independent
|
|
26
|
+
Requires-Python: >=3.7
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Dynamic: author
|
|
30
|
+
Dynamic: home-page
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
Dynamic: requires-python
|
|
33
|
+
|
|
34
|
+
# TimestampStore
|
|
35
|
+
|
|
36
|
+
Fast data structure for (id, timestamp) pairs with O(log N) operations.
|
|
37
|
+
Warning! Created by claude-opus-4.5 without human intervention.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
pip install git+https://github.com/shutkanos/timestamp_store.git
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Requirements:** C++ compiler (g++, clang++, or MSVC)
|
|
46
|
+
|
|
47
|
+
### Installing compiler
|
|
48
|
+
|
|
49
|
+
- **Ubuntu/Debian:** `sudo apt install g++`
|
|
50
|
+
- **macOS:** `xcode-select --install`
|
|
51
|
+
- **Windows:** Install [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/) or:
|
|
52
|
+
|
|
53
|
+
PowerShell:
|
|
54
|
+
```PowerShell
|
|
55
|
+
winget install -e --id MSYS2.MSYS2
|
|
56
|
+
```
|
|
57
|
+
MSYS2:
|
|
58
|
+
```
|
|
59
|
+
pacman -S mingw-w64-x86_64-gcc
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Usage
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from timestamp_store import TimestampStore
|
|
66
|
+
|
|
67
|
+
store = TimestampStore()
|
|
68
|
+
|
|
69
|
+
# Add pairs
|
|
70
|
+
store.add(1, 100)
|
|
71
|
+
store.add(2, 50)
|
|
72
|
+
store.add(3, 150)
|
|
73
|
+
|
|
74
|
+
# Remove all with timestamp < 120
|
|
75
|
+
removed = store.remove_timestamp(120)
|
|
76
|
+
print(removed) # [2, 1]
|
|
77
|
+
|
|
78
|
+
# Remove by id
|
|
79
|
+
store.remove(3)
|
|
80
|
+
|
|
81
|
+
# Create from list
|
|
82
|
+
store = TimestampStore([(1, 100), (2, 200)])
|
|
83
|
+
# Create from dict
|
|
84
|
+
store = TimestampStore({1: 100, 2: 200})
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Complexity
|
|
88
|
+
|
|
89
|
+
| Operation | Complexity |
|
|
90
|
+
|-----------|------------|
|
|
91
|
+
| `add(id, timestamp)` | O(log N) |
|
|
92
|
+
| `remove(id)` | O(log N) |
|
|
93
|
+
| `remove_timestamp(ts)` | O(K) where K = removed count |
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
setup.py
|
|
6
|
+
timestamp_store/__init__.py
|
|
7
|
+
timestamp_store/timestamp_store.dll
|
|
8
|
+
timestamp_store/wrapper.py
|
|
9
|
+
timestamp_store.egg-info/PKG-INFO
|
|
10
|
+
timestamp_store.egg-info/SOURCES.txt
|
|
11
|
+
timestamp_store.egg-info/dependency_links.txt
|
|
12
|
+
timestamp_store.egg-info/top_level.txt
|
|
13
|
+
timestamp_store/src/timestamp_store.cpp
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
timestamp_store
|