Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Release/

# xmake 缓存
.xmake/
.cache/

# clangd 编译数据库(生成产物)
compile_commands.json
Expand Down
4 changes: 2 additions & 2 deletions include/data_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ namespace write{
bool id();
bool name(int id, const std::string& new_name);
bool gender(int id, const std::string& new_gender);
bool old_point(int id, const long long& new_old_point);
bool point(int id, const long long& new_point);
bool old_score(int id, const long long& new_old_score);
bool score(int id, const long long& new_score);
bool old_rank(int id, const int& new_old_rank);
bool rank(int id, const int& new_rank);
}
Expand Down
2 changes: 0 additions & 2 deletions old/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
#include <vector>
#include <string>
#include <algorithm>
#include <limits>
#include <climits>
#include <sstream>
#include <map>
#include <cstdint>
Expand Down
236 changes: 236 additions & 0 deletions src/data_storage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
// 业务存储层:把 FileStore 引擎接到 data_storage.h 的字段级 API
// 班级语义:read::xxx(class) 激活班级(学生/规则/礼物各走独立表单例);
// write::xxx 作用于当前激活班级(无班级参数的签名按当前班级处理)
// 持久化语义:write 只改内存 + 置脏标记,由调用方在动作边界显式 save()。
// 注意:include 必须放在 import 之前——MSVC 下消费 TU 在 import 后再 include 模块全局片段已含
// 的 STL 头(<string>/<fstream> 等)会触发 C2572 默认模板参数重复定义
#include "data_storage.h"

#include <algorithm>
#include <cstring>
#include <fstream>
#include <map>
#include <stdexcept>
#include <string>

import storage;

namespace
{
std::string path_for(const std::string &class_name, const char *stem)
{
if (class_name.empty() || class_name == "default")
return std::string(stem) + ".dat";
return std::string(stem) + "_" + class_name + ".dat";
}

// 三张表各自的单例门面:学生 = FileInstance,规则/礼物 = 同构 Record 的不同类型
using RuleStore = points::FileStore<points::RuleRecord>;
using GiftStore = points::FileStore<points::GiftRecord>;

template<typename Store>
Store &ensure_store(Store * /*tag*/, const std::string &class_name, const char *stem)
{
auto path = path_for(class_name, stem);
try
{
auto &s = Store::get();
if (s.path() != path)
s.switch_to(path); // 切班级:脏则自动落盘
return s;
} catch (const std::logic_error &)
{ // 未初始化
Store::init(path);
return Store::get();
}
}

points::FileInstance &ensure_students(const std::string &class_name)
{
return ensure_store(
static_cast<points::FileInstance *>(nullptr),
class_name,
"students");
}

RuleStore &ensure_rules(const std::string &class_name)
{
return ensure_store(static_cast<RuleStore *>(nullptr), class_name, "rules");
}

GiftStore &ensure_gifts(const std::string &class_name)
{
return ensure_store(static_cast<GiftStore *>(nullptr), class_name, "gifts");
}

// UTF-8 安全截断:不超过 max_bytes,且不在多字节序列中间切断
void copy_utf8_truncated(std::byte *dest, size_t max_bytes, const std::string &src)
{
size_t n = std::min(src.size(), max_bytes);
if (n < src.size())
{ // 需要截断:回退到完整字符边界
while (n > 0 && (static_cast<unsigned char>(src[n]) & 0xC0u) == 0x80u)
--n;
}
std::memcpy(dest, src.data(), n);
std::memset(dest + n, 0, max_bytes - n);
}

std::string current_class = "default"; // read::xxx 激活,write::xxx 消费
} // namespace

namespace read
{
void students(const std::string &class_name)
{
current_class = class_name;
ensure_students(class_name); // 加载即激活
}

void rules(const std::string &class_name)
{
current_class = class_name;
ensure_rules(class_name).load(); // 强制从盘重读
}

void gifts(const std::string &class_name)
{
current_class = class_name;
ensure_gifts(class_name).load();
}

std::map<std::string, std::string> config(const std::string & /*class_name*/)
{
std::map<std::string, std::string> kv;
std::ifstream fin("config.ini");
std::string line;
while (std::getline(fin, line))
{
auto pos = line.find('=');
if (pos == std::string::npos)
continue;
kv[line.substr(0, pos)] = line.substr(pos + 1);
}
return kv;
}
} // namespace read

namespace write
{
namespace student
{
// 签名歧义:header 只声明了 bool id(),按"新建一个空学生并返回是否成功"实现
bool id()
{
ensure_students(current_class).add();
return true;
}

bool name(int id, const std::string &new_name)
{
auto &fi = ensure_students(current_class);
auto *r = fi.find(id);
if (!r) return false;
copy_utf8_truncated(r->name, sizeof(r->name), new_name);
fi.mark_dirty();
return true;
}

bool gender(int id, const std::string &new_gender)
{
auto &fi = ensure_students(current_class);
auto *r = fi.find(id);
if (!r) return false;
copy_utf8_truncated(r->gender, sizeof(r->gender), new_gender);
fi.mark_dirty();
return true;
}

bool old_score(int id, const long long &new_old_score)
{
auto &fi = ensure_students(current_class);
auto *r = fi.find(id);
if (!r) return false;
r->old_score = new_old_score;
fi.mark_dirty();
return true;
}

bool score(int id, const long long &new_score)
{
auto &fi = ensure_students(current_class);
auto *r = fi.find(id);
if (!r) return false;
r->score = new_score;
fi.mark_dirty();
return true;
}

bool old_rank(int id, const int &new_old_rank)
{
auto &fi = ensure_students(current_class);
auto *r = fi.find(id);
if (!r) return false;
r->old_rank = new_old_rank;
fi.mark_dirty();
return true;
}

bool rank(int id, const int &new_rank)
{
auto &fi = ensure_students(current_class);
auto *r = fi.find(id);
if (!r) return false;
r->rank = new_rank;
fi.mark_dirty();
return true;
}
} // namespace student

namespace rule
{
// rule_num 1..N 映射到槽位 rule_num-1(引擎 add() 分配,槽位即编号)
bool desc(int rule_num, const std::string &new_desc)
{
auto &s = ensure_rules(current_class);
auto *r = s.find(static_cast<uint32_t>(rule_num - 1));
if (!r) return false;
copy_utf8_truncated(r->desc, sizeof(r->desc), new_desc);
s.mark_dirty();
return true;
}

bool delta(int rule_num, const int &new_delta)
{
auto &s = ensure_rules(current_class);
auto *r = s.find(static_cast<uint32_t>(rule_num - 1));
if (!r) return false;
r->delta = new_delta;
s.mark_dirty();
return true;
}
} // namespace rule

namespace gift
{
bool desc(int gift_num, const std::string &new_desc)
{
auto &s = ensure_gifts(current_class);
auto *r = s.find(static_cast<uint32_t>(gift_num - 1));
if (!r) return false;
copy_utf8_truncated(r->desc, sizeof(r->desc), new_desc);
s.mark_dirty();
return true;
}

bool delta(int gift_num, const int &new_delta)
{
auto &s = ensure_gifts(current_class);
auto *r = s.find(static_cast<uint32_t>(gift_num - 1));
if (!r) return false;
r->delta = new_delta;
s.mark_dirty();
return true;
}
} // namespace gift
} // namespace write
15 changes: 15 additions & 0 deletions src/exceptions.ixx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// Created by jimmy on 2026/8/4.
//
module;
#include <stdexcept>

export module exceptions;

export namespace points
{
struct file_format_error : std::runtime_error
{
using std::runtime_error::runtime_error;
};
}
104 changes: 104 additions & 0 deletions src/storage_management.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
module;
#include <cstdint>
#include <filesystem>
#include <fstream>
#include <memory>
#include <cstring>
#include <string_view>

module storage;
import exceptions;

namespace points
{
// ---- Record(学生,161B)----
void Record::put(std::string &buf, const Record &r)
{
buf.push_back(static_cast<char>(r.flags));
auto put = [&buf](auto v)
{
buf.append(reinterpret_cast<const char *>(&v), sizeof(v));
};
put(r.id);
put(r.next);
buf.append(reinterpret_cast<const char *>(r.name), 64);
buf.append(reinterpret_cast<const char *>(r.gender), 64);
put(r.old_score);
put(r.score);
put(r.old_rank);
put(r.rank);
}

void Record::get(std::string_view s, size_t slot, Record &r)
{
const size_t off = slot * get_record_field_size();
if (s.size() < off + get_record_field_size())
throw file_format_error("Record slot out of range");
// 与 put 严格镜像:同序、同长度,跑指针逐个字段取
const char *p = s.data() + off;
r.flags = static_cast<uint8_t>(*p);
++p;
memcpy(&r.id, p, sizeof(r.id));
p += sizeof(r.id);
memcpy(&r.next, p, sizeof(r.next));
p += sizeof(r.next);
memcpy(r.name, p, 64);
p += 64;
memcpy(r.gender, p, 64);
p += 64;
memcpy(&r.old_score, p, sizeof(r.old_score));
p += sizeof(r.old_score);
memcpy(&r.score, p, sizeof(r.score));
p += sizeof(r.score);
memcpy(&r.old_rank, p, sizeof(r.old_rank));
p += sizeof(r.old_rank);
memcpy(&r.rank, p, sizeof(r.rank));
}

void Record::clear(Record &r)
{
std::memset(r.name, 0, sizeof(r.name));
std::memset(r.gender, 0, sizeof(r.gender));
r.old_score = 0;
r.score = 0;
r.old_rank = 0;
r.rank = 0;
}

// ---- RuleRecord(规则/礼物,77B;GiftRecord 继承)----
void RuleRecord::put(std::string &buf, const RuleRecord &r)
{
buf.push_back(static_cast<char>(r.flags));
auto put = [&buf](auto v)
{
buf.append(reinterpret_cast<const char *>(&v), sizeof(v));
};
put(r.id);
put(r.next);
buf.append(reinterpret_cast<const char *>(r.desc), 64);
put(r.delta);
}

void RuleRecord::get(std::string_view s, size_t slot, RuleRecord &r)
{
const size_t off = slot * rule_disk_size();
if (s.size() < off + rule_disk_size())
throw file_format_error("Rule slot out of range");
const char *p = s.data() + off;
r.flags = static_cast<uint8_t>(*p);
++p;
memcpy(&r.id, p, sizeof(r.id));
p += sizeof(r.id);
memcpy(&r.next, p, sizeof(r.next));
p += sizeof(r.next);
memcpy(r.desc, p, 64);
p += 64;
memcpy(&r.delta, p, sizeof(r.delta));
}

void RuleRecord::clear(RuleRecord &r)
{
std::memset(r.desc, 0, sizeof(r.desc));
r.delta = 0;
}
}
Loading