TkN 2.7
Toolkit for Nuclei
Loading...
Searching...
No Matches
tkensdf_reader.cpp
1/********************************************************************************
2 * Copyright (c) : Université de Lyon 1, CNRS/IN2P3, UMR5822, *
3 * IP2I, F-69622 Villeurbanne Cedex, France *
4 * Normandie Université, ENSICAEN, UNICAEN, CNRS/IN2P3, *
5 * LPC Caen, F-14000 Caen, France *
6 * Contibutor(s) : *
7 * Jérémie Dudouet jeremie.dudouet@cnrs.fr [2020] *
8 * Diego Gruyer diego.gruyer@cnrs.fr [2020] *
9 * *
10 * Licensed under the MIT License <http://opensource.org/licenses/MIT>. *
11 * SPDX-License-Identifier: MIT *
12 ********************************************************************************/
13
14#include <dirent.h>
15
16#include <algorithm>
17#include <cctype>
18#include <cstdint>
19#include <iomanip>
20#include <sstream>
21#include <string>
22#include <vector>
23
24#include "tkensdf_reader.h"
25#include "tklog.h"
26
27namespace tkn {
34}
35
36using namespace tkn;
37using namespace std;
38
39namespace {
40
41bool ascii_equivalent(std::uint32_t codepoint, char &equivalent)
42{
43 switch (codepoint) {
44 case 0x00a0: // no-break space
45 case 0x2007: // figure space
46 case 0x202f: // narrow no-break space
47 equivalent = ' ';
48 return true;
49 case 0x00b7: // middle dot
50 case 0x00d7: // multiplication sign
51 case 0x22c5: // dot operator
52 equivalent = '*';
53 return true;
54 case 0x2010: // hyphen
55 case 0x2011: // non-breaking hyphen
56 case 0x2012: // figure dash
57 case 0x2013: // en dash
58 case 0x2014: // em dash
59 case 0x2015: // horizontal bar
60 case 0x2212: // minus sign
61 equivalent = '-';
62 return true;
63 case 0x2018: // left single quotation mark
64 case 0x2019: // right single quotation mark
65 case 0x2032: // prime
66 equivalent = '\'';
67 return true;
68 case 0x201c: // left double quotation mark
69 case 0x201d: // right double quotation mark
70 case 0x2033: // double prime
71 equivalent = '"';
72 return true;
73 default:
74 return false;
75 }
76}
77
78std::string hex_value(std::uint32_t value, int width)
79{
80 std::ostringstream output;
81 output << std::uppercase << std::hex << std::setfill('0') << std::setw(width) << value;
82 return output.str();
83}
84
85bool is_comment_record(const std::string &record)
86{
87 if (record.size() < 9) return false;
88 const auto field7 = static_cast<char>(std::toupper(static_cast<unsigned char>(record[6])));
89 return field7 == 'C' || field7 == 'D' || field7 == 'T';
90}
91
92bool decode_utf8(const std::string &input, std::size_t &position, std::uint32_t &codepoint)
93{
94 const auto lead = static_cast<unsigned char>(input[position]);
95 std::size_t length = 0;
96 std::uint32_t minimum = 0;
97
98 if ((lead & 0xe0U) == 0xc0U) {
99 length = 2;
100 codepoint = lead & 0x1fU;
101 minimum = 0x80;
102 } else if ((lead & 0xf0U) == 0xe0U) {
103 length = 3;
104 codepoint = lead & 0x0fU;
105 minimum = 0x800;
106 } else if ((lead & 0xf8U) == 0xf0U) {
107 length = 4;
108 codepoint = lead & 0x07U;
109 minimum = 0x10000;
110 } else {
111 return false;
112 }
113
114 if (position + length > input.size()) return false;
115 for (std::size_t offset = 1; offset < length; ++offset) {
116 const auto byte = static_cast<unsigned char>(input[position + offset]);
117 if ((byte & 0xc0U) != 0x80U) return false;
118 codepoint = (codepoint << 6U) | (byte & 0x3fU);
119 }
120
121 if (codepoint < minimum || codepoint > 0x10ffffU ||
122 (codepoint >= 0xd800U && codepoint <= 0xdfffU))
123 return false;
124
125 position += length;
126 return true;
127}
128
129} // namespace
130
131bool tkensdf_reader::normalize_record(tkstring &record, int line_number, bool report)
132{
133 if (!record.empty() && record.back() == '\r') record.pop_back();
134
135 const bool ascii_only = std::none_of(
136 record.begin(), record.end(),
137 [](const unsigned char byte) { return byte >= 0x80U; });
138 if (ascii_only) {
139 if (record.size() == tkensdf_record::RSIZE - 1) return true;
140 if (report) {
141 glog << error_v << "record at line " << line_number << " has " << record.size()
142 << " columns instead of " << tkensdf_record::RSIZE - 1 << do_endl;
143 }
144 record.clear();
145 return false;
146 }
147
148 // Record classification uses columns 6--9. They must remain byte-identical
149 // to their logical columns before any UTF-8 substitution is considered.
150 for (std::size_t position = 0; position < 9 && position < record.size(); ++position) {
151 const auto byte = static_cast<unsigned char>(record[position]);
152 if (byte >= 0x80U) {
153 if (report) {
154 glog << error_v << "non-ASCII byte 0x" << hex_value(byte, 2)
155 << " in the structural ENSDF prefix at line " << line_number
156 << ", column " << position + 1 << do_endl;
157 }
158 record.clear();
159 return false;
160 }
161 }
162
163 const bool comment_record = is_comment_record(record);
164
165 std::string normalized;
166 normalized.reserve(record.size());
167 struct replacement {
168 std::uint32_t codepoint;
169 std::size_t column;
170 char equivalent;
171 };
172 std::vector<replacement> replacements;
173
174 for (std::size_t position = 0; position < record.size();) {
175 const auto byte = static_cast<unsigned char>(record[position]);
176 if (byte < 0x80U) {
177 normalized.push_back(static_cast<char>(byte));
178 ++position;
179 continue;
180 }
181
182 std::uint32_t codepoint = 0;
183 const auto invalid_position = position;
184 const auto column = normalized.size() + 1;
185 if (!decode_utf8(record, position, codepoint)) {
186 if (report) {
187 const auto invalid_byte = static_cast<unsigned char>(record[invalid_position]);
188 glog << error_v << "invalid UTF-8 byte 0x" << hex_value(invalid_byte, 2)
189 << " at ENSDF line " << line_number << ", column " << column << do_endl;
190 }
191 record.clear();
192 return false;
193 }
194
195 if (!comment_record || column < 10) {
196 if (report) {
197 glog << error_v << "refusing Unicode U+" << hex_value(codepoint, 4)
198 << " outside comment text at ENSDF line " << line_number
199 << ", column " << column << do_endl;
200 }
201 record.clear();
202 return false;
203 }
204
205 char equivalent = '\0';
206 if (!ascii_equivalent(codepoint, equivalent)) {
207 if (report) {
208 glog << error_v << "unsupported Unicode U+" << hex_value(codepoint, 4)
209 << " at ENSDF line " << line_number << ", column " << column << do_endl;
210 }
211 record.clear();
212 return false;
213 }
214 normalized.push_back(equivalent);
215 replacements.push_back({codepoint, column, equivalent});
216 }
217
218 if (normalized.size() != tkensdf_record::RSIZE - 1) {
219 if (report) {
220 glog << error_v << "UTF-8 normalization at ENSDF line " << line_number
221 << " produced " << normalized.size() << " columns instead of "
223 }
224 record.clear();
225 return false;
226 }
227
228 if (report) {
229 for (const auto &item : replacements) {
230 glog << warning_v << "normalized Unicode U+" << hex_value(item.codepoint, 4)
231 << " to '" << item.equivalent << "' at ENSDF line " << line_number
232 << ", column " << item.column << do_endl;
233 }
234 }
235 record = normalized;
236 return true;
237}
238
239bool tkensdf_reader::open_nuc(const tkstring &_nuc_name, const ensdf_data_type &_ftype)
240{
241 glog.set_class("ensdf_ascii_reader");
242 glog.set_method(tkstring::form("open_nuc(%s,%d)", _nuc_name.data(), _ftype));
243
244 tkstring testnuc = _nuc_name.copy().to_lower();
245 testnuc.capitalize();
246
247 tkstring file_name;
248 if (_ftype == kensdf)
249 file_name = tkstring::Form("%s/ENSDF/%s.ens", finput_folder.data(), _nuc_name.data());
250 else if (_ftype == kxundl)
251 file_name = tkstring::Form("%s/XUNDL/%s.ens", finput_folder.data(), _nuc_name.data());
252 else {
253 glog << warning_v << " database for nucleus " << _nuc_name << " not found" << do_endl;
254 glog.clear();
255 return false;
256 }
257
258 fdata_type = _ftype;
259 fNucleus = testnuc;
260 glog.clear();
261
262 return open_file(file_name);
263}
264
265bool tkensdf_reader::open_file(const tkstring &_file_name)
266{
267 glog.set_class("ensdf_ascii_reader");
268 glog.set_method(tkstring::form("open_file(%s)", _file_name.data()));
269
270 if (fEnsdf_file.is_open()) {
271 close_file();
272 }
273 fEnsdf_file.open(_file_name);
274
275 tkstring record;
276 tkensdf_record the_record;
277 tkensdf_ident_rec the_identification_record;
278
279 // counters
280 int read_record = 0;
281
282 // true if a new data set is found
283 bool new_dataset = false;
284
285 // read the file and create the list of data sets.
286 while (true) {
287 const std::streampos record_byte_position = fEnsdf_file.tellg();
288 getline(fEnsdf_file, record);
289
290 if (!fEnsdf_file.good()) break;
291
292 if (!normalize_record(record, read_record + 1, true)) {
293 read_record++;
294 continue;
295 }
296
297 if (!the_record.set_record(record)) {
298 glog << error_v << "error in : " << record << do_endl;
299 read_record++;
300 continue;
301 }
302
303 if (!the_record.is_continuation_record()) frecords_counter[the_record.get_record_type()].first++;
304 frecords_counter[the_record.get_record_type()].second++;
305
306 // end of file
307 if (!fEnsdf_file && new_dataset) {
308 the_identification_record.fposition.second = read_record;
309 fDataSets.push_back(the_identification_record);
310 the_identification_record.clear();
311 break;
312 }
313
314 read_record++;
315
316 if (the_record.get_record_type() == tkensdf_record::kident && !new_dataset) {
317 the_identification_record.set_record(record);
318 the_identification_record.set_current_position(read_record);
319 the_identification_record.set_start_position(read_record);
320 the_identification_record.set_byte_position(record_byte_position);
321
322 if (fVerbose) the_identification_record.print(std::cout);
323 new_dataset = true;
324 }
325 if (the_record.get_record_type() == tkensdf_record::kend && new_dataset) {
326 the_identification_record.set_stpop_position(read_record);
327 fDataSets.push_back(the_identification_record);
328 the_identification_record.clear();
329 new_dataset = false;
330 }
331 } // is_open
332 if (fVerbose) glog << info << fDataSets.size() << " datasets have been found in the ENSDF file " << _file_name << do_endl;
333
334 glog.clear();
335
336 return fEnsdf_file.is_open();
337}
338
340{
341 if (!fEnsdf_file.is_open()) {
342 glog << error << "No ENSDF database opened" << do_endl;
343 return;
344 }
345 tkstring data_type = "ENSDF";
346 if (fdata_type == kxundl) data_type = "XUNDL";
347 glog << info << "Database type: " << data_type << do_endl;
348 glog << info << fDataSets.size() << " datasets have been found for nucleus " << fNucleus << ":" << do_endl;
349 for (auto &key : fDataSets) key.print(std::cout);
350}
351
353{
354 // to remove any error flag
355 fEnsdf_file.clear();
356
357 if (_dataset == nullptr) return false;
358 _dataset->set_current_position(_dataset->get_start_position());
359 fEnsdf_file.seekg(_dataset->get_byte_position());
360 getline(fEnsdf_file, record);
361 normalize_record(record, _dataset->get_current_position(), false);
362 return fEnsdf_file.good();
363}
364
366{
367 if (_dataset == nullptr) return false;
368 _dataset->set_current_position(_dataset->get_current_position() + 1);
369 if (_dataset->get_current_position() >= _dataset->get_stop_position()) return false;
370 getline(fEnsdf_file, record);
371 normalize_record(record, _dataset->get_current_position(), false);
372 return fEnsdf_file.good();
373}
374
375void tkensdf_reader::close_file()
376{
377 fDataSets.clear();
378 fEnsdf_file.close();
379 fEnsdf_file.clear();
380 fNucleus.clear();
381}
382
384{
385 std::cout << std::endl;
386 glog << info << "Record counters:" << do_endl;
387
388 size_t maxsize1 = 0;
389 size_t maxsize2 = 0;
390 for (auto i : frecords_counter) {
391 if (tkstring::Form("%lld", i.second.first).length() > maxsize1) maxsize1 = tkstring::Form("%lld", i.second.first).length();
392 if (tkstring::Form("%lld", i.second.second).length() > maxsize2) maxsize2 = tkstring::Form("%lld", i.second.second).length();
393 }
394 std::cout << left << " - Record Type : counts without(with) continuation records" << std::endl;
395 for (auto i : frecords_counter) {
396 if (i.first == tkensdf_record::kident)
397 std::cout << left << " - identification : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
398 else if (i.first == tkensdf_record::khistory)
399 std::cout << left << " - History : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
400 else if (i.first == tkensdf_record::kq_value)
401 std::cout << left << " - Q-value : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
402 else if (i.first == tkensdf_record::kxref)
403 std::cout << left << " - X-ref : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
404 else if (i.first == tkensdf_record::kcomment)
405 std::cout << left << " - Comment : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
406 else if (i.first == tkensdf_record::kparent)
407 std::cout << left << " - Parent : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
408 else if (i.first == tkensdf_record::knorm)
409 std::cout << left << " - Normalisation : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
410 else if (i.first == tkensdf_record::kprodnorm)
411 std::cout << left << " - Production norm : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
412 else if (i.first == tkensdf_record::klevel)
413 std::cout << left << " - Level : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
414 else if (i.first == tkensdf_record::kbeta)
415 std::cout << left << " - Beta : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
416 else if (i.first == tkensdf_record::kec)
417 std::cout << left << " - EC : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
418 else if (i.first == tkensdf_record::kalpha)
419 std::cout << left << " - Alpha : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
420 else if (i.first == tkensdf_record::kparticle)
421 std::cout << left << " - Particle : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
422 else if (i.first == tkensdf_record::kgamma)
423 std::cout << left << " - Gamma : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
424 else if (i.first == tkensdf_record::kreference)
425 std::cout << left << " - Reference : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
426 else if (i.first == tkensdf_record::kend)
427 std::cout << left << " - End : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
428 else if (i.first == tkensdf_record::kunknown)
429 std::cout << left << " - Unknown : " << setw(maxsize1 + 1) << i.second.first << " (" << setw(maxsize2) << i.second.second << ")" << std::endl;
430 }
431 std::cout << std::endl;
432}
Decodding of the ENSDF identification record properties.
void print(std::ostream &) const override
virtual bool set_record(const tkstring &_record) override
define the record from a string
std::pair< int, int > fposition
void set_stpop_position(int _pos)
void set_current_position(int _pos)
void set_start_position(int _pos)
void set_byte_position(std::streampos _pos)
std::streampos get_byte_position() const
void print_datasets()
print the list of loaded data sets for the current nucleus and data type
bool open_nuc(const tkstring &_nuc_name, const ensdf_data_type &_ftype=kensdf)
open the file for the slected nucleus and data type, and extract the available data sets
bool first_record(tkensdf_ident_rec *_dataset, tkstring &record)
return the identification record and set its physical 1-based line position
void print_record_counters()
print record counters
bool next_record(tkensdf_ident_rec *_dataset, tkstring &record)
return the next record; on false, the terminator is not returned and record is unchanged
Decodding of the ENSDF records.
virtual bool set_record(const tkstring &_record)
define the record from a string. Option false only checks if the record is an identification record
bool is_continuation_record()
to now if the record is a continuation record or not
record_type get_record_type()
get record type
static const int RSIZE
std::string with usefull tricks from TString (ROOT) and KVString (KaliVeda) and more....
Definition tkstring.h:33
tkstring copy() const
Returns a copy of this string.
Definition tkstring.cpp:377
tkstring & to_lower()
Change all letters to lower case.
Definition tkstring.cpp:118
static const char * form(const char *_format,...)
Definition tkstring.cpp:438
static tkstring Form(const char *_format,...)
Definition tkstring.cpp:368
tkstring & capitalize()
Change first letter of string from lower to upper case.
Definition tkstring.cpp:383
Definition tklog.cpp:16
tklog & error_v(tklog &log)
Definition tklog.h:407
tklog & info(tklog &log)
Definition tklog.h:313
tklog & warning_v(tklog &log)
Definition tklog.h:386
ensdf_data_type
tklog & error(tklog &log)
Definition tklog.h:344
tklog & do_endl(tklog &log)
Definition tklog.h:212