Skip to content

Commit 11b96d1

Browse files
authored
Merge pull request #2512 from berryzplus/feature/fix_read_profile
設定ファイルの読み込み速度を改善する
2 parents c13aabf + 899e4f8 commit 11b96d1

6 files changed

Lines changed: 199 additions & 52 deletions

File tree

sakura_core/env/CProfile.cpp

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
Copyright (C) 2004, D.S.Koba, MIK, genta
1313
Copyright (C) 2006, D.S.Koba, ryoji
1414
Copyright (C) 2009, ryoji
15-
Copyright (C) 2018-2022, Sakura Editor Organization
15+
Copyright (C) 2018-2026, Sakura Editor Organization
1616
1717
SPDX-License-Identifier: Zlib
1818
*/
@@ -39,7 +39,7 @@
3939
@param line [in] 読み込んだ行
4040
*/
4141
void CProfile::_ReadOneline(
42-
const std::wstring& line
42+
std::wstring_view line
4343
)
4444
{
4545
// 空行を読み飛ばす
@@ -52,15 +52,22 @@ void CProfile::_ReadOneline(
5252
}
5353

5454
// セクション取得
55-
if (std::wsmatch m; std::regex_match(line, m, std::wregex(LR"(^\[([^=]+)\]$)"))) {
56-
m_ProfileData.emplace_back(static_cast<std::wstring>(m[1]));
55+
if (2 < line.length() && L'[' == line.front() && L']' == line.back()) {
56+
if (const auto sectionName{ line.substr(1, line.length() - 2) };
57+
std::wstring_view::npos == sectionName.find(L'=')) {
58+
m_ProfileData.emplace_back(sectionName);
59+
return;
60+
}
61+
}
62+
63+
// 最初のセクション以前の行のエントリは無視
64+
if (m_ProfileData.empty()) {
5765
return;
5866
}
5967

6068
// エントリ取得
61-
// ※最初のセクション以前の行のエントリは無視
62-
if (std::wsmatch m; !m_ProfileData.empty() && std::regex_match(line, m, std::wregex(LR"(^([^=]+)=(.*)$)"))) {
63-
m_ProfileData.back().m_Entries.try_emplace(m[1], m[2]);
69+
if (const auto pos = line.find(L'='); std::wstring_view::npos != pos && 0 < pos && pos + 1 <= line.length()) {
70+
m_ProfileData.back().m_Entries.try_emplace(std::wstring{ line.substr(0, pos) }, line.substr(pos + 1));
6471
}
6572
}
6673

@@ -91,9 +98,11 @@ bool CProfile::ReadProfile(
9198
}
9299

93100
try{
94-
while( in ){
101+
std::wstring line;
102+
103+
while (in) {
95104
//1行読込
96-
std::wstring line=in.ReadLineW();
105+
in.ReadLineW(line);
97106

98107
//解析
99108
_ReadOneline(line);

sakura_core/env/CProfile.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*/
1010
/*
1111
Copyright (C) 2003-2006, D.S.Koba
12-
Copyright (C) 2018-2025, Sakura Editor Organization
12+
Copyright (C) 2018-2026, Sakura Editor Organization
1313
1414
SPDX-License-Identifier: Zlib
1515
*/
@@ -29,7 +29,7 @@ class CProfile
2929
private:
3030
struct SectionType
3131
{
32-
using EntriesType = std::map< std::wstring, std::wstring >;
32+
using EntriesType = std::map<std::wstring, std::wstring, std::less<>>;
3333

3434
explicit SectionType(
3535
std::wstring_view name
@@ -61,7 +61,7 @@ class CProfile
6161
void DUMP( void );
6262

6363
private:
64-
void _ReadOneline(const std::wstring& line);
64+
void _ReadOneline(std::wstring_view line);
6565
bool _WriteFile(const std::filesystem::path& path, std::span<const std::wstring> lines);
6666

6767
// メンバ変数

sakura_core/io/CTextStream.cpp

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
11
/*! @file */
22
/*
3-
Copyright (C) 2018-2022, Sakura Editor Organization
3+
Copyright (C) 2018-2026, Sakura Editor Organization
44
55
SPDX-License-Identifier: Zlib
66
*/
77
#include "StdAfx.h"
8-
#include "CTextStream.h"
8+
#include "io/CTextStream.h"
9+
910
#include "charset/CCodeFactory.h"
1011
#include "charset/CShiftJis.h" // move from CCodeMediator.h 2010/6/14 Uchi
1112
#include "charset/CUtf8.h" // move from CCodeMediator.h 2010/6/14 Uchi
1213
#include "basis/CEol.h"
1314
#include "util/file.h" // _IS_REL_PATH
1415
#include "util/module.h"
1516

17+
namespace cxx {
18+
19+
std::wstring_view MultiByteToWideChar(UINT codePage, std::string_view source, std::wstring& buffer);
20+
21+
} // namespace cxx
22+
1623
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
1724
// CTextInputStream //
1825
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //
1926

2027
CTextInputStream::CTextInputStream(const WCHAR* pszPath)
2128
: CStream(pszPath,L"rb")
2229
{
23-
m_bIsUtf8=false;
24-
2530
if(Good()){
2631
//BOM確認 -> m_bIsUtf8
2732
static const BYTE UTF8_BOM[]={0xEF,0xBB,0xBF};
@@ -35,9 +40,6 @@ CTextInputStream::CTextInputStream(const WCHAR* pszPath)
3540
fseek(GetFp(),0,SEEK_SET);
3641
}
3742
}
38-
else{
39-
m_bIsUtf8 = false;
40-
}
4143
}
4244

4345
/*
@@ -52,32 +54,61 @@ CTextInputStream::~CTextInputStream()
5254
{
5355
}
5456

55-
std::wstring CTextInputStream::ReadLineW()
57+
/*!
58+
* @brief 1行読込。改行は削る
59+
*/
60+
void CTextInputStream::ReadLineW(std::wstring& line)
5661
{
5762
//$$ 非効率だけど今のところは許して。。
58-
CNativeW line;
59-
line.AllocStringBuffer(60);
60-
for (;;) {
61-
int c=getc(GetFp());
62-
if(c==EOF)break; //EOFで終了
63-
if(c=='\r'){ c=getc(GetFp()); if(c!='\n')ungetc(c,GetFp()); break; } //"\r" または "\r\n" で終了
64-
if(c=='\n')break; //"\n" で終了
65-
if( line._GetMemory()->capacity() < line._GetMemory()->GetRawLength() + 10 ){
66-
line._GetMemory()->AllocBuffer( line._GetMemory()->GetRawLength() * 2 );
63+
64+
// 実は言う程「非効率」でもない。
65+
// ・ファイルデータ → 内部バッファ m_Buffer
66+
// ・内部バッファ m_Buffer → UNICODE文字列
67+
68+
// 現在のバッファ書込位置はイテレータで管理する
69+
auto it = m_Buffer.begin();
70+
for (;; ++it) {
71+
int c = ::getc(GetFp());
72+
// CRを検出したらCRLFかどうかチェックする
73+
if ('\r' == c) {
74+
c = ::getc(GetFp());
75+
if ('\n' != c) {
76+
::ungetc(c, GetFp());
77+
c = '\r';
78+
}
6779
}
68-
line._GetMemory()->AppendRawData(&c,sizeof(char));
69-
}
7080

71-
//UTF-8 → UNICODE
72-
if(m_bIsUtf8){
73-
CUtf8::UTF8ToUnicode(*(line._GetMemory()), &line);
74-
}
75-
//Shift_JIS → UNICODE
76-
else{
77-
CShiftJis::SJISToUnicode(*(line._GetMemory()), &line);
81+
// CRLF, LF, CR, EOF で終了
82+
if ('\n' == c || '\r' == c || EOF == c) break; //EOFで終了
83+
84+
// バッファ末尾に到達したら拡張する
85+
if (it == m_Buffer.end()) {
86+
const auto pos = std::distance(m_Buffer.begin(), it);
87+
m_Buffer.resize(m_Buffer.size() * 2);
88+
it = m_Buffer.begin() + pos;
89+
}
90+
91+
// バッファに書き込む
92+
*it = static_cast<char>(c);
7893
}
7994

80-
return std::wstring(line.GetStringPtr(), line.GetStringLength()); // EOLまで。NUL文字も含める。
95+
// 変換に使うコードページを確定させる
96+
const auto codePage = m_bIsUtf8 ? CP_UTF8 : CP_SJIS; // UTF-8ならCP_UTF8、そうでなければShift_JIS(CP932)とする
97+
98+
// 変換を実行する
99+
cxx::MultiByteToWideChar(codePage, std::string_view{ m_Buffer.begin(), it }, line);
100+
}
101+
102+
/*!
103+
* @brief 1行読込。改行は削る
104+
*
105+
* @note 既存コード互換のため残しておく。
106+
*/
107+
std::wstring CTextInputStream::ReadLineW()
108+
{
109+
std::wstring line;
110+
ReadLineW(line);
111+
return line;
81112
}
82113

83114
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- //

sakura_core/io/CTextStream.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//将来はUTF-8等にすることにより、UNICODEデータの欠落が起こらないようにしたい。
99
/*
1010
Copyright (C) 2008, kobake
11-
Copyright (C) 2018-2022, Sakura Editor Organization
11+
Copyright (C) 2018-2026, Sakura Editor Organization
1212
1313
SPDX-License-Identifier: Zlib
1414
*/
@@ -32,10 +32,12 @@ class CTextInputStream : public CStream{
3232
virtual ~CTextInputStream();
3333

3434
//操作
35-
std::wstring ReadLineW(); //!< 1行読込。改行は削る
35+
void ReadLineW(std::wstring& line);
36+
std::wstring ReadLineW();
3637

3738
private:
38-
bool m_bIsUtf8; //!< UTF-8ならtrue
39+
bool m_bIsUtf8 = false; //!< UTF-8ならtrue
40+
std::string m_Buffer = std::string(4096, L'\0'); //!< 内部バッファ
3941
};
4042

4143
//テキスト出力ストリーム
@@ -68,4 +70,5 @@ class CTextInputStream_AbsIni final : public CTextInputStream{
6870
public:
6971
CTextInputStream_AbsIni(const WCHAR* pszPath, bool bOrExedir = true);
7072
};
73+
7174
#endif /* SAKURA_CTEXTSTREAM_CF4FEC73_4575_4B80_98F7_CFCBC0B433CD_H_ */

sakura_core/util/tchar_convert.cpp

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,30 @@ int MultiByteToWideChar(UINT codePage, std::string_view source, std::span<WCHAR>
9595
);
9696
}
9797

98+
/*!
99+
* ナロー文字列をワイド文字列に変換します。
100+
*
101+
* @param [in] codePage 変換に使用するコードページ。
102+
* @param [in] source 変換元のナロー文字列
103+
* @param [in, out] buffer 変換後のワイド文字列を受け取るバッファ
104+
*/
105+
std::wstring_view MultiByteToWideChar(UINT codePage, std::string_view source, std::wstring& buffer) {
106+
// 変換を実行する
107+
108+
// 変換に必要な出力バッファサイズを求める
109+
const auto required = cxx::CountAsWideChar(codePage, source);
110+
111+
// 変換に必要な出力バッファを確保する
112+
buffer.resize(required);
113+
114+
// 変換を実行する
115+
const auto converted = cxx::MultiByteToWideChar(codePage, source, std::span{ buffer });
116+
117+
buffer.resize(converted); // MultiByteToWideCharの戻り値は終端NULを含まない
118+
119+
return buffer;
120+
}
121+
98122
} // namespace cxx
99123

100124
const WCHAR* to_wchar(const ACHAR* src)
@@ -264,9 +288,7 @@ std::wstring to_wstring(std::string_view source, _In_opt_ UINT codePage) {
264288
std::wstring buffer(required, '\0');
265289

266290
// 変換を実行する
267-
const auto converted = cxx::MultiByteToWideChar(codePage, source, buffer);
268-
269-
buffer.resize(converted); // MultiByteToWideCharの戻り値は終端NULを含まない
291+
cxx::MultiByteToWideChar(codePage, source, buffer);
270292

271293
return buffer;
272294
}

src/test/cpp/tests1/test-cprofile.cpp

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,103 @@
11
/*! @file */
22
/*
3-
Copyright (C) 2018-2022, Sakura Editor Organization
3+
Copyright (C) 2018-2026, Sakura Editor Organization
44
55
SPDX-License-Identifier: Zlib
66
*/
77
#include "pch.h"
8-
#include <tchar.h>
9-
#include <Windows.h>
10-
#include <Shlwapi.h>
8+
#include "env/CDataProfile.h"
119

12-
#include "env/CProfile.h"
10+
#include <Shlwapi.h>
1311

1412
#include <cstdlib>
15-
#include <filesystem>
13+
#include <fstream>
1614

1715
#include "util/file.h"
18-
#include "env/CDataProfile.h"
16+
17+
using namespace std::literals::string_literals;
18+
using namespace std::literals::string_view_literals;
19+
20+
/*!
21+
* @brief 内部バッファが溢れたら拡張する
22+
*/
23+
TEST(CProfile, ReadProfile_ExpandLineBuffer)
24+
{
25+
std::filesystem::path iniPath{ "test.ini" };
26+
27+
std::error_code ec;
28+
std::filesystem::remove(iniPath, ec);
29+
30+
// ファイル出力ストリームをバイナリモードで開く
31+
std::ofstream os(iniPath, std::ios::binary);
32+
33+
std::vector<std::string> lines{
34+
"; test"s,
35+
"[test]"s,
36+
"test=1"s,
37+
};
38+
39+
constexpr auto& prefix = "too_long_item=";
40+
auto tooLongLine = std::format("{}{:x<4083}", prefix, 'x'); // 4096文字を超える行を作る
41+
lines.emplace_back(tooLongLine);
42+
43+
// 各行を書き込む
44+
for (const auto& line : lines) {
45+
if (!line.empty()) {
46+
os.write(LPCSTR(std::data(line)), std::size(line));
47+
}
48+
os << '\r';
49+
}
50+
51+
os.close();
52+
53+
CDataProfile cProfile;
54+
cProfile.ReadProfile(iniPath);
55+
56+
bool value = false;
57+
EXPECT_THAT(cProfile.GetProfileData(L"test", L"test", value), IsTrue());
58+
EXPECT_THAT(value, IsTrue());
59+
60+
std::filesystem::remove(iniPath, ec);
61+
}
62+
63+
/*!
64+
* @brief 設定ファイルの改行コードがCR
65+
*/
66+
TEST(CProfile, ReadProfile_LineTerminatorCr)
67+
{
68+
std::filesystem::path iniPath{ "test.ini" };
69+
70+
std::error_code ec;
71+
std::filesystem::remove(iniPath, ec);
72+
73+
// ファイル出力ストリームをバイナリモードで開く
74+
std::ofstream os(iniPath, std::ios::binary);
75+
76+
const std::array lines{
77+
u8"; test"sv,
78+
u8"[test]"sv,
79+
u8"test=1"sv,
80+
};
81+
82+
// 各行を書き込む
83+
for (const auto& line : lines) {
84+
if (!line.empty()) {
85+
os.write(LPCSTR(std::data(line)), std::size(line));
86+
}
87+
os << '\r';
88+
}
89+
90+
os.close();
91+
92+
CDataProfile cProfile;
93+
cProfile.ReadProfile(iniPath);
94+
95+
bool value = false;
96+
EXPECT_THAT(cProfile.GetProfileData(L"test", L"test", value), IsTrue());
97+
EXPECT_THAT(value, IsTrue());
98+
99+
std::filesystem::remove(iniPath, ec);
100+
}
19101

20102
/*!
21103
* @brief WriteProfileは指定されたパスに含まれるサブディレクトリを作成する

0 commit comments

Comments
 (0)