Skip to content

Commit 59e9391

Browse files
xororzclaude
andcommitted
feat: persistent prompt cache + per-model cache/ subdir
Per-prompt CLIP output is hashed (SHA-256) and persisted to {modelDir}/cache/prompt_<sha>.bin; positive and negative sides hit independently so a one-sided match still skips half the CLIP work. Prompts that resolve a TI embedding stay out of the cache. Existing mnnc and aspect-latent caches move into the same cache/ subdir; old top-level files are ignored (regenerated on next run). File manager: entering a model folder with cache/ shows a Clear Cache button with the directory's total size; cache/ is filtered out of the file list so it isn't mistaken for a 3 KB file. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5e29017 commit 59e9391

6 files changed

Lines changed: 480 additions & 111 deletions

File tree

app/src/main/cpp/src/Sha256.hpp

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
#ifndef SHA256_HPP
2+
#define SHA256_HPP
3+
4+
#include <array>
5+
#include <cstdint>
6+
#include <cstring>
7+
#include <string>
8+
9+
class Sha256 {
10+
public:
11+
Sha256() { reset(); }
12+
13+
void reset() {
14+
bitlen_ = 0;
15+
datalen_ = 0;
16+
state_[0] = 0x6a09e667;
17+
state_[1] = 0xbb67ae85;
18+
state_[2] = 0x3c6ef372;
19+
state_[3] = 0xa54ff53a;
20+
state_[4] = 0x510e527f;
21+
state_[5] = 0x9b05688c;
22+
state_[6] = 0x1f83d9ab;
23+
state_[7] = 0x5be0cd19;
24+
}
25+
26+
void update(const uint8_t* data, size_t len) {
27+
for (size_t i = 0; i < len; ++i) {
28+
data_[datalen_++] = data[i];
29+
if (datalen_ == 64) {
30+
transform();
31+
bitlen_ += 512;
32+
datalen_ = 0;
33+
}
34+
}
35+
}
36+
37+
void update(const std::string& s) {
38+
update(reinterpret_cast<const uint8_t*>(s.data()), s.size());
39+
}
40+
41+
std::array<uint8_t, 32> finalize() {
42+
uint32_t i = datalen_;
43+
if (datalen_ < 56) {
44+
data_[i++] = 0x80;
45+
while (i < 56) data_[i++] = 0x00;
46+
} else {
47+
data_[i++] = 0x80;
48+
while (i < 64) data_[i++] = 0x00;
49+
transform();
50+
std::memset(data_, 0, 56);
51+
}
52+
bitlen_ += static_cast<uint64_t>(datalen_) * 8;
53+
for (int j = 7; j >= 0; --j) {
54+
data_[56 + (7 - j)] = static_cast<uint8_t>((bitlen_ >> (j * 8)) & 0xff);
55+
}
56+
transform();
57+
58+
std::array<uint8_t, 32> out{};
59+
for (int j = 0; j < 4; ++j) {
60+
for (int k = 0; k < 8; ++k) {
61+
out[j + k * 4] =
62+
static_cast<uint8_t>((state_[k] >> (24 - j * 8)) & 0xff);
63+
}
64+
}
65+
return out;
66+
}
67+
68+
// Returns the first hex_chars hex characters of SHA-256(s).
69+
// hex_chars must be <= 64; values are silently clamped to 64.
70+
static std::string hashHex(const std::string& s, size_t hex_chars = 32) {
71+
Sha256 h;
72+
h.update(s);
73+
auto digest = h.finalize();
74+
static const char* kHex = "0123456789abcdef";
75+
if (hex_chars > 64) hex_chars = 64;
76+
size_t bytes = (hex_chars + 1) / 2;
77+
std::string out;
78+
out.reserve(bytes * 2);
79+
for (size_t i = 0; i < bytes; ++i) {
80+
out.push_back(kHex[(digest[i] >> 4) & 0xf]);
81+
out.push_back(kHex[digest[i] & 0xf]);
82+
}
83+
out.resize(hex_chars);
84+
return out;
85+
}
86+
87+
private:
88+
uint8_t data_[64];
89+
uint32_t datalen_;
90+
uint64_t bitlen_;
91+
uint32_t state_[8];
92+
93+
static uint32_t rotr(uint32_t x, uint32_t n) {
94+
return (x >> n) | (x << (32 - n));
95+
}
96+
97+
void transform() {
98+
static const uint32_t K[64] = {
99+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
100+
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
101+
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
102+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
103+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
104+
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
105+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
106+
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
107+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
108+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
109+
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
110+
};
111+
112+
uint32_t m[64];
113+
for (uint32_t i = 0, j = 0; i < 16; ++i, j += 4) {
114+
m[i] = (uint32_t(data_[j]) << 24) | (uint32_t(data_[j + 1]) << 16) |
115+
(uint32_t(data_[j + 2]) << 8) | uint32_t(data_[j + 3]);
116+
}
117+
for (uint32_t i = 16; i < 64; ++i) {
118+
uint32_t s0 = rotr(m[i - 15], 7) ^ rotr(m[i - 15], 18) ^ (m[i - 15] >> 3);
119+
uint32_t s1 = rotr(m[i - 2], 17) ^ rotr(m[i - 2], 19) ^ (m[i - 2] >> 10);
120+
m[i] = m[i - 16] + s0 + m[i - 7] + s1;
121+
}
122+
123+
uint32_t a = state_[0], b = state_[1], c = state_[2], d = state_[3];
124+
uint32_t e = state_[4], f = state_[5], g = state_[6], h = state_[7];
125+
for (uint32_t i = 0; i < 64; ++i) {
126+
uint32_t S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
127+
uint32_t ch = (e & f) ^ (~e & g);
128+
uint32_t t1 = h + S1 + ch + K[i] + m[i];
129+
uint32_t S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
130+
uint32_t mj = (a & b) ^ (a & c) ^ (b & c);
131+
uint32_t t2 = S0 + mj;
132+
h = g;
133+
g = f;
134+
f = e;
135+
e = d + t1;
136+
d = c;
137+
c = b;
138+
b = a;
139+
a = t1 + t2;
140+
}
141+
142+
state_[0] += a;
143+
state_[1] += b;
144+
state_[2] += c;
145+
state_[3] += d;
146+
state_[4] += e;
147+
state_[5] += f;
148+
state_[6] += g;
149+
state_[7] += h;
150+
}
151+
};
152+
153+
#endif // SHA256_HPP

0 commit comments

Comments
 (0)