Introduce til::some (#4123)

## Summary of the Pull Request
Introduces a type that is basically an array (stack allocated, fixed size) that reports size based on how many elements are actually filled (from the front), iterates only the filled ones, and has some basic vector push/pop semantics.

## PR Checklist
* [x] I work here
* [x] I work here
* [x] I work here
* [ ] I'd love to roll this out to SomeViewports.... maybe in this commit or a follow on one.
* [ ] We need a TIL tests library and I should test this there. 

## Detailed Description of the Pull Request / Additional comments
The original gist of this was used for `SomeViewports` which was a struct to hold between 0 and 4 viewports, based on how many were left after subtraction (since rectangle subtraction functions in Windows code simply fail for resultants that yield >=2 rectangle regions.)

I figured now that we're TIL-ifying useful common utility things that this would be best suited to a template because I'm certain there are other circumstances where we would like to iterate a partially filled array and want it to not auto-resize-up like a vector would.

## Validation Steps Performed
* [ ] TIL tests added
This commit is contained in:
Michael Niksa 2020-01-09 09:07:52 -08:00 committed by GitHub
parent d4c527607a
commit 735d2e5613
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 603 additions and 1 deletions

View File

@ -257,6 +257,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.LIB", "src\wincon
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winconpty.DLL", "src\winconpty\dll\winconptydll.vcxproj", "{A22EC5F6-7851-4B88-AC52-47249D437A52}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "til.unit.tests", "src\til\ut_til\til.unit.tests.vcxproj", "{767268EE-174A-46FE-96F0-EEE698A1BBC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
AuditMode|Any CPU = AuditMode|Any CPU
@ -1318,6 +1320,26 @@ Global
{A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|x64.Build.0 = Release|x64
{A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|x86.ActiveCfg = Release|Win32
{A22EC5F6-7851-4B88-AC52-47249D437A52}.Release|x86.Build.0 = Release|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|ARM64.Build.0 = AuditMode|ARM64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|x64.ActiveCfg = Release|x64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|x86.ActiveCfg = AuditMode|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.AuditMode|x86.Build.0 = AuditMode|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|Any CPU.ActiveCfg = Debug|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|ARM64.ActiveCfg = Debug|ARM64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|ARM64.Build.0 = Debug|ARM64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x64.ActiveCfg = Debug|x64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x64.Build.0 = Debug|x64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x86.ActiveCfg = Debug|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Debug|x86.Build.0 = Debug|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|Any CPU.ActiveCfg = Release|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|ARM64.ActiveCfg = Release|ARM64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|ARM64.Build.0 = Release|ARM64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x64.ActiveCfg = Release|x64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x64.Build.0 = Release|x64
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x86.ActiveCfg = Release|Win32
{767268EE-174A-46FE-96F0-EEE698A1BBC9}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -1385,6 +1407,7 @@ Global
{B0AC39D6-7B40-49A9-8202-58549BAE1FB1} = {59840756-302F-44DF-AA47-441A9D673202}
{58A03BB2-DF5A-4B66-91A0-7EF3BA01269A} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{A22EC5F6-7851-4B88-AC52-47249D437A52} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB}
{767268EE-174A-46FE-96F0-EEE698A1BBC9} = {89CDCC5C-9F53-4054-97A4-639D99F169CD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271}

View File

@ -8,6 +8,7 @@ DIRS=\
server \
terminal \
testlist \
til \
tools \
tsf \
types \

View File

@ -3,6 +3,8 @@
#pragma once
#include "til/some.h"
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
// The at function declares that you've already sufficiently checked that your array access

184
src/inc/til/some.h Normal file
View File

@ -0,0 +1,184 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#pragma once
#include <array>
namespace til // Terminal Implementation Library. Also: "Today I Learned"
{
template<class T, size_t N>
class some
{
private:
std::array<T, N> _array;
size_t _used;
#ifdef UNIT_TESTING
friend class SomeTests;
#endif
public:
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using iterator = typename decltype(_array)::iterator;
using const_iterator = typename decltype(_array)::const_iterator;
using reverse_iterator = typename decltype(_array)::reverse_iterator;
using const_reverse_iterator = typename decltype(_array)::const_reverse_iterator;
some() noexcept :
_array{},
_used{ 0 }
{
}
some(std::initializer_list<T> init)
{
if (init.size() > N)
{
_invalidArg();
}
std::copy(init.begin(), init.end(), _array.begin());
_used = init.size();
}
void fill(const T& _Value)
{
_array.fill(_Value);
_used = N;
}
void swap(some& _Other) noexcept(std::is_nothrow_swappable<T>::value)
{
_array.swap(_Other._array);
std::swap(_used, _Other._used);
}
constexpr const_iterator begin() const noexcept
{
return _array.begin();
}
constexpr const_iterator end() const noexcept
{
return _array.begin() + _used;
}
constexpr const_reverse_iterator rbegin() const noexcept
{
return const_reverse_iterator(end());
}
constexpr const_reverse_iterator rend() const noexcept
{
return const_reverse_iterator(begin());
}
constexpr const_iterator cbegin() const noexcept
{
return begin();
}
constexpr const_iterator cend() const noexcept
{
return end();
}
constexpr const_reverse_iterator crbegin() const noexcept
{
return rbegin();
}
constexpr const_reverse_iterator crend() const noexcept
{
return rend();
}
constexpr size_type size() const noexcept
{
return _used;
}
constexpr size_type max_size() const noexcept
{
return N;
}
constexpr bool empty() const noexcept
{
return !_used;
}
constexpr const_reference at(size_type pos) const
{
if (_used <= pos)
{
_outOfRange();
}
return _array[pos];
}
constexpr const_reference operator[](size_type pos) const noexcept
{
return _array[pos];
}
constexpr const_reference front() const noexcept
{
return _array[0];
}
constexpr const_reference back() const noexcept
{
return _array[_used - 1];
}
constexpr const T* data() const noexcept
{
return _array.data();
}
void push_back(const T& val)
{
if (_used >= N)
{
_outOfRange();
}
_array[_used] = val;
++_used;
}
void pop_back()
{
if (_used <= 0)
{
_outOfRange();
}
--_used;
_array[_used] = 0;
}
[[noreturn]] void _invalidArg() const
{
throw std::invalid_argument("invalid argument");
}
[[noreturn]] void _outOfRange() const
{
throw std::out_of_range("invalid some<T, N> subscript");
}
};
}

2
src/til/dirs Normal file
View File

@ -0,0 +1,2 @@
DIRS=ut_til \

4
src/til/precomp.cpp Normal file
View File

@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"

31
src/til/precomp.h Normal file
View File

@ -0,0 +1,31 @@
/*++
Copyright (c) Microsoft Corporation
Licensed under the MIT license.
Module Name:
- precomp.h
Abstract:
- Contains external headers to include in the precompile phase of console build process.
- Avoid including internal project headers. Instead include them only in the classes that need them (helps with test project building).
--*/
#pragma once
// clang-format off
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
// Windows Header Files:
#include <windows.h>
// This includes support libraries from the CRT, STL, WIL, and GSL
#include "LibraryIncludes.h"
// clang-format on

View File

@ -0,0 +1,12 @@
//Autogenerated file name + version resource file for Device Guard whitelisting effort
#include <windows.h>
#include <ntverp.h>
#define VER_FILETYPE VFT_UNKNOWN
#define VER_FILESUBTYPE VFT2_UNKNOWN
#define VER_FILEDESCRIPTION_STR ___TARGETNAME
#define VER_INTERNALNAME_STR ___TARGETNAME
#define VER_ORIGINALFILENAME_STR ___TARGETNAME
#include "common.ver"

View File

@ -0,0 +1,275 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
#include "precomp.h"
#include "WexTestClass.h"
using namespace WEX::Common;
using namespace WEX::Logging;
using namespace WEX::TestExecution;
class SomeTests
{
TEST_CLASS(SomeTests);
TEST_METHOD(Construct)
{
Log::Comment(L"Default Constructor");
til::some<int, 2> s;
Log::Comment(L"Valid Initializer List Constructor");
til::some<int, 2> t{ 1 };
til::some<int, 2> u{ 1, 2 };
Log::Comment(L"Invalid Initializer List Constructor");
auto f = []() {
til::some<int, 2> v{ 1, 2, 3 };
};
VERIFY_THROWS(f(), std::invalid_argument);
}
TEST_METHOD(Fill)
{
til::some<int, 4> s;
const auto val = 12;
s.fill(val);
VERIFY_ARE_EQUAL(s.max_size(), s.size());
for (const auto& i : s)
{
VERIFY_ARE_EQUAL(val, i);
}
}
TEST_METHOD(Swap)
{
til::some<int, 4> a;
til::some<int, 4> b;
const auto aVal = 900;
a.fill(900);
const auto bVal = 45;
b.push_back(45);
const auto aSize = a.size();
const auto bSize = b.size();
a.swap(b);
VERIFY_ARE_EQUAL(aSize, b.size());
VERIFY_ARE_EQUAL(bSize, a.size());
VERIFY_ARE_EQUAL(bVal, a[0]);
for (const auto& i : b)
{
VERIFY_ARE_EQUAL(aVal, i);
}
}
TEST_METHOD(Size)
{
til::some<int, 2> c;
VERIFY_ARE_EQUAL(0u, c.size());
c.push_back(3);
VERIFY_ARE_EQUAL(1u, c.size());
c.push_back(12);
VERIFY_ARE_EQUAL(2u, c.size());
c.pop_back();
VERIFY_ARE_EQUAL(1u, c.size());
c.pop_back();
VERIFY_ARE_EQUAL(0u, c.size());
}
TEST_METHOD(MaxSize)
{
til::some<int, 2> c;
VERIFY_ARE_EQUAL(2u, c.max_size());
c.push_back(3);
VERIFY_ARE_EQUAL(2u, c.max_size());
c.push_back(12);
VERIFY_ARE_EQUAL(2u, c.size());
c.pop_back();
VERIFY_ARE_EQUAL(2u, c.max_size());
c.pop_back();
VERIFY_ARE_EQUAL(2u, c.max_size());
}
TEST_METHOD(PushBack)
{
til::some<int, 1> s;
s.push_back(12);
VERIFY_THROWS(s.push_back(12), std::out_of_range);
}
TEST_METHOD(PopBack)
{
til::some<int, 1> s;
VERIFY_THROWS(s.pop_back(), std::out_of_range);
s.push_back(12);
VERIFY_THROWS(s.push_back(12), std::out_of_range);
}
TEST_METHOD(Empty)
{
til::some<int, 2> s;
VERIFY_IS_TRUE(s.empty());
s.push_back(12);
VERIFY_IS_FALSE(s.empty());
s.pop_back();
VERIFY_IS_TRUE(s.empty());
}
TEST_METHOD(Data)
{
til::some<int, 2> s;
const auto one = 1;
const auto two = 2;
s.push_back(one);
s.push_back(two);
auto data = s.data();
VERIFY_ARE_EQUAL(one, *data);
VERIFY_ARE_EQUAL(two, *(data + 1));
}
TEST_METHOD(FrontBack)
{
til::some<int, 2> s;
const auto one = 1;
const auto two = 2;
s.push_back(one);
s.push_back(two);
VERIFY_ARE_EQUAL(one, s.front());
VERIFY_ARE_EQUAL(two, s.back());
}
TEST_METHOD(Indexing)
{
const auto one = 14;
const auto two = 28;
til::some<int, 2> s;
VERIFY_THROWS(s.at(0), std::out_of_range);
VERIFY_THROWS(s.at(1), std::out_of_range);
auto a = s[0];
a = s[1];
s.push_back(one);
VERIFY_ARE_EQUAL(one, s.at(0));
VERIFY_ARE_EQUAL(one, s[0]);
VERIFY_THROWS(s.at(1), std::out_of_range);
a = s[1];
s.push_back(two);
VERIFY_ARE_EQUAL(one, s.at(0));
VERIFY_ARE_EQUAL(one, s[0]);
VERIFY_ARE_EQUAL(two, s.at(1));
VERIFY_ARE_EQUAL(two, s[1]);
s.pop_back();
VERIFY_ARE_EQUAL(one, s.at(0));
VERIFY_ARE_EQUAL(one, s[0]);
VERIFY_THROWS(s.at(1), std::out_of_range);
a = s[1];
s.pop_back();
VERIFY_THROWS(s.at(0), std::out_of_range);
VERIFY_THROWS(s.at(1), std::out_of_range);
a = s[0];
a = s[1];
}
TEST_METHOD(ForwardIter)
{
const int vals[] = { 17, 99 };
const int valLength = ARRAYSIZE(vals);
til::some<int, 2> s;
VERIFY_ARE_EQUAL(s.begin(), s.end());
VERIFY_ARE_EQUAL(s.cbegin(), s.cend());
VERIFY_ARE_EQUAL(s.begin(), s.cbegin());
VERIFY_ARE_EQUAL(s.end(), s.cend());
s.push_back(vals[0]);
s.push_back(vals[1]);
VERIFY_ARE_EQUAL(s.begin() + valLength, s.end());
VERIFY_ARE_EQUAL(s.cbegin() + valLength, s.cend());
auto count = 0;
for (const auto& i : s)
{
VERIFY_ARE_EQUAL(vals[count], i);
++count;
}
VERIFY_ARE_EQUAL(valLength, count);
count = 0;
for (auto i = s.cbegin(); i < s.cend(); ++i)
{
VERIFY_ARE_EQUAL(vals[count], *i);
++count;
}
VERIFY_ARE_EQUAL(valLength, count);
count = 0;
for (auto i = s.begin(); i < s.end(); ++i)
{
VERIFY_ARE_EQUAL(vals[count], *i);
++count;
}
VERIFY_ARE_EQUAL(valLength, count);
}
TEST_METHOD(ReverseIter)
{
const int vals[] = { 17, 99 };
const int valLength = ARRAYSIZE(vals);
til::some<int, 2> s;
VERIFY_ARE_EQUAL(s.rbegin(), s.rend());
VERIFY_ARE_EQUAL(s.crbegin(), s.crend());
VERIFY_ARE_EQUAL(s.rbegin(), s.crbegin());
VERIFY_ARE_EQUAL(s.rend(), s.crend());
s.push_back(vals[0]);
s.push_back(vals[1]);
VERIFY_ARE_EQUAL(s.rbegin() + valLength, s.rend());
VERIFY_ARE_EQUAL(s.crbegin() + valLength, s.crend());
auto count = 0;
for (auto i = s.crbegin(); i < s.crend(); ++i)
{
VERIFY_ARE_EQUAL(vals[valLength - count - 1], *i);
++count;
}
VERIFY_ARE_EQUAL(valLength, count);
count = 0;
for (auto i = s.rbegin(); i < s.rend(); ++i)
{
VERIFY_ARE_EQUAL(vals[valLength - count - 1], *i);
++count;
}
VERIFY_ARE_EQUAL(valLength, count);
}
};

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="ProductBuild" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(NTMAKEENV)\UniversalTest\Microsoft.TestInfrastructure.UniversalTest.props" />
</Project>

33
src/til/ut_til/sources Normal file
View File

@ -0,0 +1,33 @@
!include ..\..\project.unittest.inc
# -------------------------------------
# Program Information
# -------------------------------------
TARGETNAME = Microsoft.Terminal.Til.UnitTests
TARGETTYPE = DYNLINK
DLLDEF =
# -------------------------------------
# Sources, Headers, and Libraries
# -------------------------------------
SOURCES = \
$(SOURCES) \
SomeTests.cpp \
DefaultResource.rc \
INCLUDES = \
.. \
$(INCLUDES) \
TARGETLIBS = \
$(TARGETLIBS) \
# -------------------------------------
# Localization
# -------------------------------------
# Autogenerated. Sets file name for Device Guard whitelisting effort, used in RC.exe.
C_DEFINES = $(C_DEFINES) -D___TARGETNAME="""$(TARGETNAME).$(TARGETTYPE)"""
MUI_VERIFY_NO_LOC_RESOURCE = 1

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{767268EE-174A-46FE-96F0-EEE698A1BBC9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>tilunittests</RootNamespace>
<ProjectName>til.unit.tests</ProjectName>
<TargetName>til.Unit.Tests</TargetName>
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<Import Project="$(SolutionDir)src\common.build.pre.props" />
<ItemGroup>
<ClCompile Include="SomeTests.cpp" />
<ClCompile Include="..\precomp.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\precomp.h" />
</ItemGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>..;$(SolutionDir)src\inc;$(SolutionDir)src\inc\test;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<!-- Careful reordering these. Some default props (contained in these files) are order sensitive. -->
<Import Project="$(SolutionDir)src\common.build.post.props" />
<Import Project="$(SolutionDir)src\common.build.tests.props" />
</Project>

View File

@ -159,7 +159,7 @@ function Invoke-OpenConsoleTests()
[switch]$FTOnly,
[parameter(Mandatory=$false)]
[ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp')]
[ValidateSet('host', 'interactivityWin32', 'terminal', 'adapter', 'feature', 'uia', 'textbuffer', 'til', 'types', 'terminalCore', 'terminalApp', 'localTerminalApp')]
[string]$Test,
[parameter(Mandatory=$false)]

View File

@ -11,6 +11,7 @@ call %TAEF% ^
%OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\ConParser.Unit.Tests.dll ^
%OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\ConAdapter.Unit.Tests.dll ^
%OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\Types.Unit.Tests.dll ^
%OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\til.unit.tests.dll ^
%OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\UnitTests_TerminalApp\Terminal.App.Unit.Tests.dll ^
%OPENCON%\bin\%PLATFORM%\%_LAST_BUILD_CONF%\LocalTests_TerminalApp\TerminalApp.LocalTests.dll ^
%*

View File

@ -9,6 +9,7 @@
<test name="terminal" type="unit" binary="ConParser.Unit.Tests.dll" />
<test name="adapter" type="unit" binary="ConAdapter.Unit.Tests.dll" />
<test name="types" type="unit" binary="Types.Unit.Tests.dll" />
<test name="til" type="unit" binary="til.unit.tests.dll" />
<test name="feature" type="ft" binary="Conhost.Feature.Tests.dll" />
<test name="uia" type="ft" binary="Conhost.UIA.Tests.dll" />
</tests>