| 214 | } |
| 215 | |
| 216 | DartsDictPtr DartsDict::NewFromFile(FILE* fp) { |
| 217 | DartsDictPtr dict(new DartsDict()); |
| 218 | auto internal = dict->internal; |
| 219 | |
| 220 | size_t headerLen = strlen(OCDHEADER); |
| 221 | std::vector<char> headerBuffer(headerLen); |
| 222 | size_t bytesRead = fread(headerBuffer.data(), sizeof(char), headerLen, fp); |
| 223 | if (bytesRead != headerLen || memcmp(headerBuffer.data(), OCDHEADER, headerLen) != 0) { |
| 224 | throw InvalidFormat("Invalid OpenCC dictionary header"); |
| 225 | } |
| 226 | |
| 227 | // Measure remaining bytes for bounds checking |
| 228 | long currentOffset = ftell(fp); |
| 229 | fseek(fp, 0L, SEEK_END); |
| 230 | long fileEnd = ftell(fp); |
| 231 | fseek(fp, currentOffset, SEEK_SET); |
| 232 | size_t remainingSize = |
| 233 | (fileEnd > currentOffset) |
| 234 | ? static_cast<size_t>(fileEnd - currentOffset) |
| 235 | : 0; |
| 236 | |
| 237 | // Detect 32-bit vs 64-bit unit size by reading 8 bytes and checking |
| 238 | // whether bytes [4..7] are all zero. |
| 239 | // - Old 64-bit build: dartsSize field is uint64_t (8 bytes); high 32 bits |
| 240 | // are zero for any realistic file size → bytes [4..7] == 0. |
| 241 | // - 32-bit build (new or old): dartsSize field is uint32_t (4 bytes); |
| 242 | // bytes [4..7] are the first 4 bytes of the darts array (non-zero for |
| 243 | // any valid array whose root unit has a non-zero offset). |
| 244 | uint8_t probe[8]; |
| 245 | if (fread(probe, 1, 8, fp) != 8) { |
| 246 | throw InvalidFormat("Invalid OpenCC dictionary header (dartsSize)"); |
| 247 | } |
| 248 | bool is64bit = |
| 249 | (probe[4] == 0 && probe[5] == 0 && probe[6] == 0 && probe[7] == 0); |
| 250 | |
| 251 | if (is64bit) { |
| 252 | uint64_t dartsSize64; |
| 253 | memcpy(&dartsSize64, probe, 8); |
| 254 | size_t dartsSize = static_cast<size_t>(dartsSize64); |
| 255 | if (dartsSize > remainingSize) { |
| 256 | throw InvalidFormat( |
| 257 | "Invalid OpenCC dictionary (dartsSize exceeds file size)"); |
| 258 | } |
| 259 | if (dartsSize % sizeof(LegacyUnit64) != 0) { |
| 260 | throw InvalidFormat("Invalid legacy OCD dictionary unit alignment"); |
| 261 | } |
| 262 | std::unique_ptr<void, decltype(&free)> buffer(malloc(dartsSize), free); |
| 263 | if (!buffer) { |
| 264 | throw std::bad_alloc(); |
| 265 | } |
| 266 | bytesRead = fread(buffer.get(), 1, dartsSize, fp); |
| 267 | if (bytesRead != dartsSize) { |
| 268 | throw InvalidFormat("Invalid legacy OCD dictionary size mismatch"); |
| 269 | } |
| 270 | |
| 271 | BinaryDictPtr binary = BinaryDict::NewFromFile(fp); |
| 272 | |
| 273 | internal->buffer = buffer.release(); |
nothing calls this directly
no test coverage detected