|
13 | 13 | #include "../types/inc/convert.hpp"
|
14 | 14 | #include "../../types/inc/GlyphWidth.hpp"
|
15 | 15 |
|
16 |
| -namespace |
17 |
| -{ |
18 |
| - struct BufferAllocator |
19 |
| - { |
20 |
| - BufferAllocator(til::size sz) |
21 |
| - { |
22 |
| - const auto w = gsl::narrow<uint16_t>(sz.width); |
23 |
| - const auto h = gsl::narrow<uint16_t>(sz.height); |
24 |
| - |
25 |
| - const auto charsBytes = w * sizeof(wchar_t); |
26 |
| - // The ROW::_indices array stores 1 more item than the buffer is wide. |
27 |
| - // That extra column stores the past-the-end _chars pointer. |
28 |
| - const auto indicesBytes = w * sizeof(uint16_t) + sizeof(uint16_t); |
29 |
| - const auto rowStride = charsBytes + indicesBytes; |
30 |
| - // 65535*65535 cells would result in a charsAreaSize of 8GiB. |
31 |
| - // --> Use uint64_t so that we can safely do our calculations even on x86. |
32 |
| - const auto allocSize = gsl::narrow<size_t>(::base::strict_cast<uint64_t>(rowStride) * ::base::strict_cast<uint64_t>(h)); |
33 |
| - |
34 |
| - _buffer = wil::unique_virtualalloc_ptr<std::byte>{ static_cast<std::byte*>(VirtualAlloc(nullptr, allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) }; |
35 |
| - THROW_IF_NULL_ALLOC(_buffer); |
36 |
| - |
37 |
| - _data = std::span{ _buffer.get(), allocSize }.begin(); |
38 |
| - _rowStride = rowStride; |
39 |
| - _indicesOffset = charsBytes; |
40 |
| - _width = w; |
41 |
| - _height = h; |
42 |
| - } |
43 |
| - |
44 |
| - BufferAllocator& operator++() noexcept |
45 |
| - { |
46 |
| - _data += _rowStride; |
47 |
| - return *this; |
48 |
| - } |
49 |
| - |
50 |
| - wchar_t* chars() const noexcept |
51 |
| - { |
52 |
| - return til::bit_cast<wchar_t*>(&*_data); |
53 |
| - } |
54 |
| - |
55 |
| - uint16_t* indices() const noexcept |
56 |
| - { |
57 |
| - return til::bit_cast<uint16_t*>(&*(_data + _indicesOffset)); |
58 |
| - } |
59 |
| - |
60 |
| - uint16_t width() const noexcept |
61 |
| - { |
62 |
| - return _width; |
63 |
| - } |
64 |
| - |
65 |
| - uint16_t height() const noexcept |
66 |
| - { |
67 |
| - return _height; |
68 |
| - } |
69 |
| - |
70 |
| - wil::unique_virtualalloc_ptr<std::byte>&& take() noexcept |
71 |
| - { |
72 |
| - return std::move(_buffer); |
73 |
| - } |
74 |
| - |
75 |
| - private: |
76 |
| - wil::unique_virtualalloc_ptr<std::byte> _buffer; |
77 |
| - std::span<std::byte>::iterator _data; |
78 |
| - size_t _rowStride; |
79 |
| - size_t _indicesOffset; |
80 |
| - uint16_t _width; |
81 |
| - uint16_t _height; |
82 |
| - }; |
83 |
| -} |
84 |
| - |
85 | 16 | using namespace Microsoft::Console;
|
86 | 17 | using namespace Microsoft::Console::Types;
|
87 | 18 |
|
@@ -111,16 +42,7 @@ TextBuffer::TextBuffer(til::size screenBufferSize,
|
111 | 42 | // Guard against resizing the text buffer to 0 columns/rows, which would break being able to insert text.
|
112 | 43 | screenBufferSize.width = std::max(screenBufferSize.width, 1);
|
113 | 44 | screenBufferSize.height = std::max(screenBufferSize.height, 1);
|
114 |
| - |
115 |
| - BufferAllocator allocator{ screenBufferSize }; |
116 |
| - |
117 |
| - _storage.reserve(allocator.height()); |
118 |
| - for (til::CoordType i = 0; i < screenBufferSize.height; ++i, ++allocator) |
119 |
| - { |
120 |
| - _storage.emplace_back(allocator.chars(), allocator.indices(), allocator.width(), _currentAttributes); |
121 |
| - } |
122 |
| - |
123 |
| - _charBuffer = allocator.take(); |
| 45 | + _charBuffer = _allocateBuffer(screenBufferSize, _currentAttributes, _storage); |
124 | 46 | _UpdateSize();
|
125 | 47 | }
|
126 | 48 |
|
@@ -775,6 +697,37 @@ const Viewport TextBuffer::GetSize() const noexcept
|
775 | 697 | return _size;
|
776 | 698 | }
|
777 | 699 |
|
| 700 | +wil::unique_virtualalloc_ptr<std::byte> TextBuffer::_allocateBuffer(til::size sz, const TextAttribute& attributes, std::vector<ROW>& rows) |
| 701 | +{ |
| 702 | + const auto w = gsl::narrow<uint16_t>(sz.width); |
| 703 | + const auto h = gsl::narrow<uint16_t>(sz.height); |
| 704 | + |
| 705 | + const auto charsBytes = w * sizeof(wchar_t); |
| 706 | + // The ROW::_indices array stores 1 more item than the buffer is wide. |
| 707 | + // That extra column stores the past-the-end _chars pointer. |
| 708 | + const auto indicesBytes = w * sizeof(uint16_t) + sizeof(uint16_t); |
| 709 | + const auto rowStride = charsBytes + indicesBytes; |
| 710 | + // 65535*65535 cells would result in a charsAreaSize of 8GiB. |
| 711 | + // --> Use uint64_t so that we can safely do our calculations even on x86. |
| 712 | + const auto allocSize = gsl::narrow<size_t>(::base::strict_cast<uint64_t>(rowStride) * ::base::strict_cast<uint64_t>(h)); |
| 713 | + |
| 714 | + auto buffer = wil::unique_virtualalloc_ptr<std::byte>{ static_cast<std::byte*>(VirtualAlloc(nullptr, allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)) }; |
| 715 | + THROW_IF_NULL_ALLOC(buffer); |
| 716 | + |
| 717 | + auto data = std::span{ buffer.get(), allocSize }.begin(); |
| 718 | + |
| 719 | + rows.resize(h); |
| 720 | + for (auto& row : rows) |
| 721 | + { |
| 722 | + const auto chars = til::bit_cast<wchar_t*>(&*data); |
| 723 | + const auto indices = til::bit_cast<uint16_t*>(&*(data + charsBytes)); |
| 724 | + row = { chars, indices, w, attributes }; |
| 725 | + data += rowStride; |
| 726 | + } |
| 727 | + |
| 728 | + return buffer; |
| 729 | +} |
| 730 | + |
778 | 731 | void TextBuffer::_UpdateSize()
|
779 | 732 | {
|
780 | 733 | _size = Viewport::FromDimensions({ _storage.at(0).size(), gsl::narrow<til::CoordType>(_storage.size()) });
|
@@ -1001,37 +954,55 @@ void TextBuffer::Reset()
|
1001 | 954 |
|
1002 | 955 | try
|
1003 | 956 | {
|
1004 |
| - BufferAllocator allocator{ newSize }; |
1005 |
| - |
1006 |
| - const auto currentSize = GetSize().Dimensions(); |
1007 |
| - const auto attributes = GetCurrentAttributes(); |
1008 |
| - |
1009 | 957 | til::CoordType TopRow = 0; // new top row of the screen buffer
|
1010 | 958 | if (newSize.height <= GetCursor().GetPosition().y)
|
1011 | 959 | {
|
1012 | 960 | TopRow = GetCursor().GetPosition().y - newSize.height + 1;
|
1013 | 961 | }
|
1014 |
| - const auto TopRowIndex = (GetFirstRowIndex() + TopRow) % currentSize.height; |
| 962 | + const auto TopRowIndex = gsl::narrow_cast<size_t>(_firstRow + TopRow) % _storage.size(); |
1015 | 963 |
|
1016 |
| - // rotate rows until the top row is at index 0 |
1017 |
| - std::rotate(_storage.begin(), _storage.begin() + TopRowIndex, _storage.end()); |
1018 |
| - _SetFirstRowIndex(0); |
1019 |
| - |
1020 |
| - // realloc in the Y direction |
1021 |
| - // remove rows if we're shrinking |
1022 |
| - _storage.resize(allocator.height()); |
| 964 | + std::vector<ROW> newStorage; |
| 965 | + auto newBuffer = _allocateBuffer(newSize, _currentAttributes, newStorage); |
1023 | 966 |
|
1024 |
| - // realloc in the X direction |
1025 |
| - for (auto& it : _storage) |
| 967 | + // This basically imitates a std::rotate_copy(first, mid, last), but uses ROW::CopyRangeFrom() to do the copying. |
1026 | 968 | {
|
1027 |
| - it.Resize(allocator.chars(), allocator.indices(), allocator.width(), attributes); |
1028 |
| - ++allocator; |
| 969 | + const auto first = _storage.begin(); |
| 970 | + const auto last = _storage.end(); |
| 971 | + const auto mid = first + TopRowIndex; |
| 972 | + auto dest = newStorage.begin(); |
| 973 | + |
| 974 | + std::span<ROW> sourceRanges[]{ |
| 975 | + { mid, last }, |
| 976 | + { first, mid }, |
| 977 | + }; |
| 978 | + |
| 979 | + // Ensure we don't copy more from `_storage` than fit into `newStorage`. |
| 980 | + if (sourceRanges[0].size() > newStorage.size()) |
| 981 | + { |
| 982 | + sourceRanges[0] = sourceRanges[0].subspan(0, newStorage.size()); |
| 983 | + } |
| 984 | + if (const auto remaining = newStorage.size() - sourceRanges[0].size(); sourceRanges[1].size() > remaining) |
| 985 | + { |
| 986 | + sourceRanges[1] = sourceRanges[1].subspan(0, remaining); |
| 987 | + } |
| 988 | + |
| 989 | + for (const auto& sourceRange : sourceRanges) |
| 990 | + { |
| 991 | + for (const auto& oldRow : sourceRange) |
| 992 | + { |
| 993 | + til::CoordType begin = 0; |
| 994 | + dest->CopyRangeFrom(0, til::CoordTypeMax, oldRow, begin, til::CoordTypeMax); |
| 995 | + dest->TransferAttributes(oldRow.Attributes(), newSize.width); |
| 996 | + ++dest; |
| 997 | + } |
| 998 | + } |
1029 | 999 | }
|
1030 | 1000 |
|
1031 |
| - // Update the cached size value |
1032 |
| - _UpdateSize(); |
| 1001 | + _charBuffer = std::move(newBuffer); |
| 1002 | + _storage = std::move(newStorage); |
1033 | 1003 |
|
1034 |
| - _charBuffer = allocator.take(); |
| 1004 | + _SetFirstRowIndex(0); |
| 1005 | + _UpdateSize(); |
1035 | 1006 | }
|
1036 | 1007 | CATCH_RETURN();
|
1037 | 1008 |
|
|
0 commit comments