From 681952701e06d427ce4abe11d8d559a755dfdc06 Mon Sep 17 00:00:00 2001 From: Alex Spataru Date: Fri, 5 Feb 2021 10:48:29 -0500 Subject: [PATCH] Allow copying & exporting console data --- assets/qml/Windows/Console.qml | 91 +++++++++++++++++++++++++++++++++ assets/qml/Windows/Setup.qml | 5 +- assets/translations/de.qm | Bin 15200 -> 15678 bytes assets/translations/de.ts | 24 +++++++++ assets/translations/en.qm | Bin 8596 -> 8884 bytes assets/translations/en.ts | 24 +++++++++ assets/translations/es.qm | Bin 15408 -> 15908 bytes assets/translations/es.ts | 24 +++++++++ assets/translations/zh.qm | Bin 10419 -> 10753 bytes assets/translations/zh.ts | 24 +++++++++ src/IO/Console.cpp | 68 +++++++++++++++++++++++- src/IO/Console.h | 6 +++ src/IO/Manager.cpp | 4 ++ 13 files changed, 266 insertions(+), 4 deletions(-) diff --git a/assets/qml/Windows/Console.qml b/assets/qml/Windows/Console.qml index f4ad7970..b3ab722a 100644 --- a/assets/qml/Windows/Console.qml +++ b/assets/qml/Windows/Console.qml @@ -41,6 +41,13 @@ Control { // property alias text: textArea.text readonly property color consoleColor: "#8ecd9d" + + // + // Hacks to allow context menu to work + // + property int curPos + property int selectEnd + property int selectStart // // Function to send through serial port data @@ -62,6 +69,42 @@ Control { property alias displayMode: displayModeCombo.currentIndex } + // + // Shortcut to copy selection of console + // + Shortcut { + sequence: StandardKey.Copy + onActivated: textArea.copy() + } + + // + // Right-click context menu + // + Menu { + id: contextMenu + + MenuItem { + text: qsTr("Copy") + opacity: enabled ? 1 : 0.5 + onTriggered: textArea.copy() + enabled: textArea.selectedText + } + + MenuItem { + text: qsTr("Clear") + opacity: enabled ? 1 : 0.5 + enabled: textArea.length > 0 + onTriggered: Cpp_IO_Console.clear() + } + + MenuItem { + opacity: enabled ? 1 : 0.5 + text: qsTr("Save as") + "..." + onTriggered: Cpp_IO_Console.save() + enabled: Cpp_IO_Console.saveAvailable + } + } + // // Controls // @@ -111,18 +154,54 @@ Control { TextEdit { id: textArea + focus: true readOnly: true font.pixelSize: 12 width: flick.width height: flick.height + selectByMouse: true + selectByKeyboard: true color: root.consoleColor + persistentSelection: true wrapMode: TextEdit.NoWrap font.family: app.monoFont + selectionColor: palette.highlight + selectedTextColor: palette.highlightedText onCursorRectangleChanged: flick.ensureVisible(cursorRectangle) onTextChanged: { if (Cpp_IO_Console.autoscroll) textArea.cursorPosition = textArea.length } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.RightButton + + onClicked: { + root.selectStart = textArea.selectionStart; + root.selectEnd = textArea.selectionEnd; + root.curPos = textArea.cursorPosition; + contextMenu.x = mouse.x; + contextMenu.y = mouse.y; + contextMenu.open(); + textArea.cursorPosition = root.curPos; + textArea.select(root.selectStart, root.selectEnd); + } + + onPressAndHold: { + if (mouse.source === Qt.MouseEventNotSynthesized) { + root.selectStart = textArea.selectionStart; + root.selectEnd = textArea.selectionEnd; + root.curPos = textArea.cursorPosition; + contextMenu.x = mouse.x; + contextMenu.y = mouse.y; + contextMenu.open(); + textArea.cursorPosition = root.curPos; + textArea.select(root.selectStart, root.selectEnd); + } + } + } } } } @@ -246,6 +325,18 @@ Control { Button { height: 24 + Layout.maximumWidth: 32 + icon.color: palette.text + opacity: enabled ? 1 : 0.5 + onClicked: Cpp_IO_Console.save() + icon.source: "qrc:/icons/save.svg" + enabled: Cpp_IO_Console.saveAvailable + Behavior on opacity {NumberAnimation{}} + } + + Button { + height: 24 + Layout.maximumWidth: 32 icon.color: palette.text opacity: enabled ? 1 : 0.5 enabled: textArea.length > 0 diff --git a/assets/qml/Windows/Setup.qml b/assets/qml/Windows/Setup.qml index 33f911f6..215ee03d 100644 --- a/assets/qml/Windows/Setup.qml +++ b/assets/qml/Windows/Setup.qml @@ -186,7 +186,7 @@ Control { // Label { opacity: enabled ? 1 : 0.5 - enabled: !Cpp_IO_Serial.connected + enabled: !Cpp_IO_Manager.connected Behavior on opacity {NumberAnimation{}} text: qsTr("COM Port") + ":" } ComboBox { @@ -200,7 +200,7 @@ Control { } opacity: enabled ? 1 : 0.5 - enabled: !Cpp_IO_Serial.connected + enabled: !Cpp_IO_Manager.connected Behavior on opacity {NumberAnimation{}} } @@ -209,7 +209,6 @@ Control { // Label { opacity: enabled ? 1 : 0.5 - enabled: !Cpp_IO_Serial.connected Behavior on opacity {NumberAnimation{}} text: qsTr("Baud Rate") + ":" } ComboBox { diff --git a/assets/translations/de.qm b/assets/translations/de.qm index e8c0a270b751903e259593bcfa31cbc2292cfb3e..54bd6dfe340f0d5ccff84ff379cc1180ac439fb2 100644 GIT binary patch delta 1446 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 PR{w2hmBt@P*b?Show timestamp Zeitstempel anzeigen + + Copy + Kopieren + + + Clear + Löschen + + + Save as + Speichern als + CsvPlayer @@ -459,6 +471,18 @@ Hexadecimal Hexadezimal + + Export console data + Konsolendaten exportieren + + + Text files + Textdateien + + + File save error + Fehler beim Speichern der Datei + IO::DataSources::Serial diff --git a/assets/translations/en.qm b/assets/translations/en.qm index 8f6f9ea008878470afb0ea05a944f46426e123a4..555f764d4e53bb98d3bcd31eee0396a79ec4f82c 100644 GIT binary patch delta 1208 zcmX9+YfzMB6h6D(b-&9lEDOf&3h01iLpS0wF)@V@8Y@C7kc5V-EC?|yElAR+TPls0 zadM$Cs(OVxz+~xB`x_DX~H378K;%FnVl9s+PMCA5)hnCfTPu}l; zG*^%yCX)NPO;sJ}^METC5|AIkwJp=Y zUgbG%&WJ>1h7Y~V5Sd@#*U7i=PzRqEYbRm``GVp|%rS!B_8RQbkMf>dn3pDkZ!jOh z7@pv}SB$~laQ>fvA>Y_41l?^T3Q&aj9M~-_6f$C7C9-}d9B#qBNXbH1S1!gdC0rcX zOl}f>7Oq>Tq5QJ&)2Vzg9}>Cx1@QHTIBysa&2JG?+i)M)EavtlVGL33mEONdSbpwI|pO)gz zgu21RCdExUKtPb}52B!ilN`%`N8usKaSUdB7IuF z9}eY7Gb%8&x3f3 z>TGerk-t@!EdsM%uI^~4#x6{$hsUQN@Phg!`!%dDQ3rBvqkf$_H#`agC3+@%_jUx1 z>ldABhO%IN?SK{5oJ=VUG-5~H>Vv)n{^7twij!kryRCxb9?Q)1HthSdsLhPm9 zK>MJq)tUT~wv;$sWg#1##ih<7M@X){% zC!vQJp_H8jE7Jv*x*U;d1%ymeOe0n)pfsl;3?XYh#b3{T-tYT9@AE$Idq>l%7FJFT z%>k4=z_}90Yy|Gv2;?3FqAtMRR}Q!qKx{m~oKc7;ZW41K{?NksKad`MlXv z)W1OBw>BX5E;4J@0>%a`ZDWD`29|ZS0^WQqFJyjn1B$#mfS5+Szn?;U+tFCbx1kdw z#K+Kc?nCkxqwkq4VCqX47&#A!J8aWGdmaL`rQwHd(^B0_?0G>N;5=ympm;WUGVH&f z@21ex_K`=@WC`Tw!7jn!rVe_l3 zlD7%R4=o0y#7T5l34L`QvRo8K%VK~j#ll2eJz&#BC8L>jrD9}&^=hnm=)Ik^b(Qqc zIofKBNjb@hfLpHGX8)^dFKIMsg+Usaha+6lk zwpOLKf%o9(S6W)uaePtf=zfv)?`4k-~3jwzDqA?V6xI$9)Kz|MEQY zq-rTg*&B7`ybWk~5t6S3TI8K*#+0&YQH@wO+q*oh$O*G2Hu~ zCPs|xJl0#sh^%vNHA>#t&G#?H-kB2l{xBk!{{K5gOz+-dCd@GtR`e3PO#ioMc|mdJ z>deQfJjvY9P(~;IG|K{W0d3ISURTA*M6>qV2=OQLOYtkl4YNCckoDu{MDG;}*k%c@ zZDpf$YxShow timestamp + + Copy + + + + Clear + + + + Save as + + CsvPlayer @@ -396,6 +408,18 @@ Hexadecimal + + Export console data + + + + Text files + + + + File save error + + IO::DataSources::Serial diff --git a/assets/translations/es.qm b/assets/translations/es.qm index d1077f49fcaf36bc42af1179e8ceb6a61ccb1037..8049ddd17134c1fb6061db24d8229e46f92e61e4 100644 GIT binary patch delta 1467 zcmZWo3s6*L6#jPay?d9tdv}3Vd>}s2O^VbFF1Wg~wk!k_xxFmP zM-+yUk2HKRlVOU^$Y^Ss`6^BFk&Q#6B|av{260SmhQ5V{rfGNfKl`71&Ue1^opZk| z+g4E(s% z8wY9BDq0^TM+WnBkP7IJ`W@2e#Ey=Dbp9j|atsM6ZM650kQxevhG5dmR{?+D7EG@B z27ynPP0B{*&^K7XjLeZA(OcPV`)onx_0z!c*N|2FA`t!%^G`ER`WiWBj{;A>kLL;* zKd=sqBi{rP3h?PB5=yH?eHHKF@w9ccYjF9(XT)tpOZH6IkkX3Q_KSe=E8W07MS!iN zI$9Syaqt1XL!%&l7wNaS5`mGO`WqLUDNwz>efSArSdCzAiw6>I!h|_Fz@Xd0RNJ#` z_>wR+nKs5=U7?HhhYS;z7}MErs!;wG)6@jvz^)m9lGAO6IE0qX5x@wm@X*@}cq&=w zIK3Uvi=sTWk$JgdT`Tj0Tg6?UtfbBjQq&C6x1W>-U!cyBG1A;IsX$nZw79B?IPXZM z@fMqMhq~--4%eOUWOM;H(BV2p!FnF%~%tGoN`jb3w@lT@kdp^he^Y2|9G^I1ASX-3b-h&|O7$E(8!) zD1tGMn><@d`I!wRE>Onq^T%-`rYK_{Xn;9UaXv|5CozgP?KdXAt!O*wPij)u2#-0r zEM;x4m$*3%%EqcolyblFMeSO^Fkg8z-OR}sDv!1{aC16MGnbqIhKeTbXgrnFn^xYZ z^aE_Bdv`d(>FoZ^4^DJXfv-LDiQFGx$f!+&T68Gg83vV%hzuIx_R}wg5$rUPBP-@jOaQAl3WSOO=IhxXJuzWv}1j52BZEns^ zcii&$4;^6o#hQ6Ll`m%h}FS&F(C2EVXS9c8o#>%82J-hVP$@K&FqLV%UkI@pRMEjZ{Rh zCvfk3QGRv$5k zNA%!YC%N33y*NNt^Ljnr0A^!U+`GE8{+PhjJS-+$>3?_zpvwZ>dr*|QTA9yYK&av_ KfNk-s!~O;Zx^6!J delta 1066 zcmX9-dr(w$7(Ki9-o4A+y}QeTq6n)PyI`V=@-Tx^X-s(uX`qya7VCqY92v_=^&D3f5>)h}DzUMjLce?2OTb?QI zXh6;eY#V{JI$&x$kaZe(Y&*0)TY)eEV%;ggVu#o?0GN#szi$SnzYXb$4~XX>WiO`x zQ;^p2|CDSWumx8vDuw^?T?mXx;vZeaYwc@Qb_hiUB_ z#~=kMKX(8#FCy*eE5M{&tmpoyww03CLW#Dn2U>vjqvoeH5%X3&STu2UB34|;Vl4JhI{iVeko7++@}6cLSkuRNOQv}C*dFrR zFU`0{UMKIBo==DaCN7mWc$ygBD!nNuQIB4!VTp!gJ}He^n@Q)S1G?~j0dQvOUNYQa zfk(P^^Vd@37j-*cBOf6*bftGFjCO^tD)u&%eJo3U^ z@@q|!v*KSSZ*B7NGh7I3q1@b@2iWuEF5eq$V6J>KWSE0qm2cIrr<~L3^+DY%5TBr5 z*1(6^q58be#Uwmczp0ax=@orh(TBj?O25^a4=8An-^IJFOf2}_w~B1Up=hr71L96a z(7y_VUQl9xiXrjGl*Gnx3cg!OeBc6%PG#+*So-HEuGHT+@Fm6dv6~OhVda4EkSq9w zQWCg@O7$xzJU7_L-^yo4O90&}WhC9m6&z4TPMzas{AI#ULH5 zs(p1gaafIQzrq65>YB6#?7Udre5L^KYf=kR<^X}etM65n@rD}J;9Du=uE=CowT=^9QC#I`@~f43n4SNOg|*CfeG)J5(hU?D^08E;a^+| zL$5jIUL2RH*Syt#J}0X+mlrazK`@_7Ji^MDfJ`0HUNHShow timestamp Marca de tiempo + + Copy + Copiar + + + Clear + Limpiar + + + Save as + Guardar como + CsvPlayer @@ -491,6 +503,18 @@ Hexadecimal Hexadecimal + + Export console data + Exportar datos de la consola + + + Text files + Archivos de texto + + + File save error + Error al intentar guardar el archivo + IO::DataSources::Serial diff --git a/assets/translations/zh.qm b/assets/translations/zh.qm index 519c3b898ef07279f47b0e5b0097df7c9450dd57..819d1378f1195745d6fbd20b73a27c3b677d426f 100644 GIT binary patch delta 1319 zcmX|94NQ~;7=G@)zq{}5yORUtH#ilTO`17@GEobL4n&N95K}Ms6o_$p;15l`GEL2L z6oQ#z5h^xNmQ-psWtt{hHt?r(N-GnE)-e1h*Lu*6+ivgoz2EcxJkR^IrN5n39pYU8 zC^>-d*FfrCz<&*py$cB32xE68FmegRJyn4D2*kY&fO;O{;eNjV2Jy$EK)_l^FK%Ky z1t}+qbu%F?WZq{7q-OT^IRWYC9{|e+jExy!+=sET4#rMQUw#?z`t8Duy6-XcdlH@X zNEyc^%ymeKt7SIlX|x9;<@XNYnfplH_Bvqy2YDT=6K){?Y&$Tj7IPL6AG8X^!S4XU zm+)yl7m6sw-fF(vS1~p+9>k^2&)K&M-I=rDi!ma4?p^?-V}__N%K>d_O{gI(A>
    M)Lif%fw&@V=dHjH|rNUsxZosfWRFc~O zYn0g7!+Q0h*z(Cb>T8oi(zw3=4C&cU>O5k%lr=dPupN?$s}BRtAZeMBKzzM)V1@x+ z*b}5d#}F_`j_4BzsFYu`+~A<=^1?|jK?| zV!3@*5A|InKYUF70ntiW-vJ=Rqr5zydRyOBvg76fW9BK_n`jton{xDM5%2I=Ioq|= z%T1z{OQHA3{F?Gh>mm-CY?8K(A_LhJaFv514w}*q@OyZpspw1+?@(nbJ443ObkmlV z9|EIW&2wM9!$Y^2-(Pl~{RH!OH&1X-uDRDCaxsT$IOhWx+f~6-NGB($F;_?^JWY-N z${WE=rmFFGblUu)>iRc^1yfXg=ATsJxT^1A-r1{uAUvW~`_#>TOUP`5x}*9c^V8IQ z+cs07EcL-GD;I87A2jTzbH1?5E0hBdKJnWnDj|AK7X7yO$>%J;&-_pUyeg ztr^}NHgNP?Gjdqy`>VC|y^k1!tXs!PEU;S}FF#L4C0g*-0p?e0k#n!oa+9=}Q|CC~ zl(rys8kKWs?j{dCldXADCh*o~ZOzUt^ulRv`>neqcv?Fm9-|`NTG#xWtaodJSFdw{ zZ*6mh4Xc?rZ;NW(O^=SZ)pdnZn&q~i61adZ&o)rT+ZiU<9^E!jp$2=(tyr2h(_ZN_ ziOd@8byaMvX!iZ_AJg+K_S4zNsYqZ65cyVHy13o^LWZ-h$V62j^DiZq=`N4LCneEc zUg0j&4FX@pd@Unx7$hdT7gyC>(P9JT%$|b0w3mjzD4DKhy3L@Av!tJf9!W^Lh6d zh6?=gzEyzi0ivG;a)UrjBT#ek#8p6g`T#Kq z$&<4C42DF4#qDkMWv$-SbQ3v9AzU(xd{4s zwpodt#7tbh^cnNkV*G_oKw=o->C1q0*0Ai6Jt5BVSM>DZnGM!iLSothZIjj^4sSM}+KDq8)*Q2B%Ny7plzE(po3f zzDt^UuW-62A29XKqhn4OZ(j^JyM+feF-*5bm>WJ07|KLBJ49Z;*cm2Yc~9*5xSsk> zNsnHlt_xD7!gc9@o+VZK&yfF6dQ;9|+zP30qk;FoN^|xB%4zE|CEpbQm&f$7b%F(k zOciU(0ddl_Zx{8jr<(#3G{z7xwL98qXr*Z|FHAkNP5=DMdE<21dAAR66w2#MsjnI% z7p>U}EL|_Rc5xxp4f4RicIrMZkBq)S{#WwVga;h-ru=*F4%!);XI?nQ0!c0A{60Q7 z>&@HGXHxK5bM<*nCS{o$-#SDi4p~Z`zfVJZEZ^Mzo|8{lChZ~{pH>VPqX6TeBA8!c z!@ntMH&U5rmE!JCX5l4@`@WZ3dqpXKn8wCkiubv{$UCNZKlky$HKKed%+i^?N<+-+ zbSk2>`LFOkQTei^fl3rAGkGeV{Z^UjILXcU!n&zy2#7DSdIwXe*b~z|BKu{~oeTGumwb#08xD!QMP0ngSYu4UrZ{$C8LTkM<%?VFvUyElcb+tBHdYkEFV~m#9_L_DdedkEl_}PL&R_#tSa(ON>6Kx%K0C#wkUMNSchb2e zw`{wk)^f5sTT?9)%bM+^`v`Zs!S++pcT{B2P9S+(XusHM33&CS&?)_f5|SM*L&y`~ Gt^W_kP#HD= diff --git a/assets/translations/zh.ts b/assets/translations/zh.ts index a8f0bc76..0e5bbbb4 100644 --- a/assets/translations/zh.ts +++ b/assets/translations/zh.ts @@ -155,6 +155,18 @@ Show timestamp 显示时间戳 + + Copy + 复制 + + + Clear + 删除 + + + Save as + 另存为 + CsvPlayer @@ -487,6 +499,18 @@ Hexadecimal 十六进制 + + Export console data + 导出控制台数据 + + + Text files + 文字档 + + + File save error + 文件保存错误 + IO::DataSources::Serial diff --git a/src/IO/Console.cpp b/src/IO/Console.cpp index 1b8e4c4e..c1a6477c 100644 --- a/src/IO/Console.cpp +++ b/src/IO/Console.cpp @@ -23,13 +23,21 @@ #include "Console.h" #include "Manager.h" +#include #include #include +#include +#include #include using namespace IO; static Console *INSTANCE = nullptr; +/** + * Set buffer size to 15 MB + */ +static const int MAX_BUFFER_SIZE = 1024 * 1024 * 15; + /** * Constructor function */ @@ -45,6 +53,7 @@ Console::Console() , m_cursor(nullptr) , m_document(nullptr) { + clear(); auto m = Manager::getInstance(); connect(m, &Manager::dataReceived, this, &Console::onDataReceived); LOG_INFO() << "Class initialized"; @@ -79,6 +88,14 @@ bool Console::autoscroll() const return m_autoscroll; } +/** + * Returns @c true if data buffer contains information that the user can export. + */ +bool Console::saveAvailable() const +{ + return m_dataBuffer.size() > 0; +} + /** * Returns @c true if a timestamp should be shown before each displayed data block. */ @@ -190,15 +207,49 @@ QStringList Console::displayModes() const void Console::copy() { } -void Console::save() { } +/** + * Allows the user to export the information displayed on the console + */ +void Console::save() +{ + // No data buffer received, abort + if (!saveAvailable()) + return; + + // Get file name + auto path + = QFileDialog::getSaveFileName(Q_NULLPTR, tr("Export console data"), + QDir::homePath(), tr("Text files") + " (*.txt)"); + + // Create file + if (!path.isEmpty()) + { + QFile file(path); + if (file.open(QFile::WriteOnly)) + { + file.write(m_dataBuffer); + file.close(); + + Misc::Utilities::revealFile(path); + } + + else + Misc::Utilities::showMessageBox(tr("File save error"), file.errorString()); + } +} /** * Deletes all the text displayed by the current QML text document */ void Console::clear() { + // Clear console display if (document()) document()->clear(); + + // Reserve 15 MB for data buffer + m_dataBuffer.clear(); + m_dataBuffer.reserve(MAX_BUFFER_SIZE); } /** @@ -383,10 +434,25 @@ void Console::setTextDocument(QQuickTextDocument *document) * Displays the given @a data in the console. @c QByteArray to ~@c QString conversion is * done by the @c dataToString() function, which displays incoming data either in UTF-8 * or in hexadecimal mode. + * + * The incoming data is also added to a buffer so that the user can save the file if + * needed. */ void Console::onDataReceived(const QByteArray &data) { + // Display data append(dataToString(data)); + + // Append data to buffer + m_dataBuffer.append(data); + if (m_dataBuffer.size() > MAX_BUFFER_SIZE) + { + m_dataBuffer.clear(); + m_dataBuffer.reserve(MAX_BUFFER_SIZE); + } + + // Notify UI + emit dataReceived(); } /** diff --git a/src/IO/Console.h b/src/IO/Console.h index 8e690ef6..7802d8f4 100644 --- a/src/IO/Console.h +++ b/src/IO/Console.h @@ -48,6 +48,9 @@ class Console : public QObject READ showTimestamp WRITE setShowTimestamp NOTIFY showTimestampChanged) + Q_PROPERTY(bool saveAvailable + READ saveAvailable + NOTIFY dataReceived) Q_PROPERTY(IO::Console::DataMode dataMode READ dataMode WRITE setDataMode @@ -67,6 +70,7 @@ class Console : public QObject signals: void echoChanged(); + void dataReceived(); void dataModeChanged(); void autoscrollChanged(); void lineEndingChanged(); @@ -103,6 +107,7 @@ public: bool echo() const; bool autoscroll() const; + bool saveAvailable() const; bool showTimestamp() const; DataMode dataMode() const; @@ -145,6 +150,7 @@ private: private: DataMode m_dataMode; + QByteArray m_dataBuffer; LineEnding m_lineEnding; DisplayMode m_displayMode; diff --git a/src/IO/Manager.cpp b/src/IO/Manager.cpp index ee483b19..958bd809 100644 --- a/src/IO/Manager.cpp +++ b/src/IO/Manager.cpp @@ -46,6 +46,7 @@ Manager::Manager() , m_finishSequence("*/") { setWatchdogInterval(15); + setMaxBufferSize(1024 * 1024); LOG_INFO() << "Class initialized"; } @@ -310,6 +311,7 @@ void Manager::disconnectDevice() m_device = nullptr; m_receivedBytes = 0; m_dataBuffer.clear(); + m_dataBuffer.reserve(maxBufferSize()); // Update UI emit deviceChanged(); @@ -351,6 +353,8 @@ void Manager::setMaxBufferSize(const int maxBufferSize) { m_maxBuzzerSize = maxBufferSize; emit maxBufferSizeChanged(); + + m_dataBuffer.reserve(maxBufferSize); } /**