feat: add lab-rv32i-freertos-allocator-resource card
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
# K06 — MemoryResource and FreeRtosAllocator<T>
|
||||
|
||||
## Position
|
||||
|
||||
- Series: FreeRTOS C++
|
||||
- Lesson: L05, card K06
|
||||
- Duration: 30 minutes
|
||||
- Runtime: FreeRTOS `heap_4.c` plus a 256-byte `.bss` arena on Hazard3
|
||||
- C++ mode: freestanding C++17, no exceptions, RTTI, vtables or hosted allocator
|
||||
|
||||
K05 selected and measured one kernel heap. K06 adds an explicit runtime
|
||||
resource boundary so identical typed code can use either that heap or caller
|
||||
owned static storage.
|
||||
|
||||
## Outcome
|
||||
|
||||
After 30 minutes the student can draw the three-word resource object, explain
|
||||
why it is not virtual inheritance, implement overflow-safe `count*sizeof(T)`,
|
||||
classify addresses by storage domain, and state the different release policies
|
||||
of a general heap and monotonic arena.
|
||||
|
||||
## Lesson plan — 30 minutes
|
||||
|
||||
| Time | Mode | Evidence |
|
||||
| --- | --- | --- |
|
||||
| 0–5 | contract | draw context + allocate/deallocate functions |
|
||||
| 5–10 | typed count | complete and justify the overflow guard |
|
||||
| 10–16 | two resources | allocate 16 `uint32_t` values from heap and arena |
|
||||
| 16–21 | address proof | classify `ucHeap` versus `.bss`; check alignment |
|
||||
| 21–26 | failure | overflow and capacity failures change no storage state |
|
||||
| 26–30 | release | compare heap free, arena no-op deallocate and reset |
|
||||
|
||||
## Runtime dispatch without vtable
|
||||
|
||||
```cpp
|
||||
struct MemoryResource {
|
||||
void* context;
|
||||
void* (*allocate)(void*, size_t, size_t) noexcept;
|
||||
void (*deallocate)(void*, void*, size_t, size_t) noexcept;
|
||||
};
|
||||
```
|
||||
|
||||
The provider owns state; the view stores a non-owning context. `HeapResource`
|
||||
counts attempts and delegates to FreeRTOS. `StaticArenaResource` stores base,
|
||||
capacity and current offset. The ELF must have no vtable/typeinfo.
|
||||
|
||||
## Typed overflow contract
|
||||
|
||||
For `T` and `count`:
|
||||
|
||||
```text
|
||||
count == 0 -> nullptr
|
||||
count > SIZE_MAX / sizeof(T) -> nullptr, resource not called
|
||||
otherwise bytes=count*sizeof(T) -> resource.allocate(bytes, alignof(T))
|
||||
```
|
||||
|
||||
The failure must not wrap to a small allocation. Both resource counters and
|
||||
storage state are captured before and after the overflow request.
|
||||
|
||||
## Canonical experiment
|
||||
|
||||
```text
|
||||
HeapResource --MemoryResource--> FreeRtosAllocator<uint32_t> -> 16 values
|
||||
address inside ucHeap; deallocate -> baseline immediately
|
||||
|
||||
StaticArenaResource --MemoryResource--> same typed allocator -> 16 values
|
||||
address inside g_static_arena_storage (.bss)
|
||||
deallocate -> used remains 64
|
||||
reset -> used becomes 0
|
||||
```
|
||||
|
||||
Both addresses satisfy `address % alignof(uint32_t) == 0` and both buffers
|
||||
produce the predicted sums.
|
||||
|
||||
## Stable evidence
|
||||
|
||||
| Evidence | Required relation |
|
||||
| --- | --- |
|
||||
| resource representation | context and function pointers are non-null; allocate functions differ |
|
||||
| heap address | lies inside `ucHeap`, aligned for `uint32_t` |
|
||||
| arena address | lies in global arena range, outside `ucHeap`, aligned |
|
||||
| overflow | both return null before provider failure counters increase |
|
||||
| capacity failure | heap available and arena used remain unchanged |
|
||||
| heap policy | successful=1, failed=1, deallocations=1; final free=baseline |
|
||||
| arena policy | successful=1, failed=1; used 64 after deallocate, 0 after reset |
|
||||
|
||||
## Main traps
|
||||
|
||||
1. Multiply only after the division guard; detecting overflow after wrapping is
|
||||
too late.
|
||||
2. A resource view does not own its context; provider lifetime must dominate.
|
||||
3. Arena `deallocate` is not heap free. Reset invalidates all arena allocations
|
||||
at once and therefore needs an external lifetime boundary.
|
||||
4. An alignment greater than the provider guarantee must fail explicitly.
|
||||
5. This fallible allocator is not ordinary `new` and is not advertised as a
|
||||
complete hosted/STL allocator.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- host ASan/UBSan test validates alignment, overflow and no-op/reset behavior;
|
||||
- `MemoryResource` occupies three pointers and all resource classes are
|
||||
non-polymorphic;
|
||||
- ABI has no vtable, RTTI, unwind, dynamic initializer or global new/delete;
|
||||
- Hazard3 proves two storage domains and identical 16-element operations;
|
||||
- all failure and release relations pass at checkpoint 7.
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
% Generated from json/card_source.json by tools/render_card.py.
|
||||
% Do not edit manually; update the JSON and regenerate.
|
||||
|
||||
\documentclass[12pt,a4paper]{article}
|
||||
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage{lmodern}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[polish]{babel}
|
||||
\usepackage{csquotes}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{xcolor}
|
||||
\usepackage[a4paper,left=2.15cm,right=2.15cm,top=0.5cm,bottom=0.5cm,headheight=24mm,headsep=3mm,includehead]{geometry}
|
||||
\usepackage{eso-pic}
|
||||
\usepackage{marginnote}
|
||||
\usepackage{array}
|
||||
\usepackage{tabularx}
|
||||
\usepackage{graphicx}
|
||||
\usepackage{pdflscape}
|
||||
\usepackage{float}
|
||||
\usepackage{silence}
|
||||
\WarningFilter{soulutf8}{This package is obsolete}
|
||||
\usepackage{pdfcomment}
|
||||
\usepackage{enumitem}
|
||||
\usepackage{needspace}
|
||||
\usepackage{fancyhdr}
|
||||
\usepackage{lastpage}
|
||||
\usepackage{microtype}
|
||||
\usepackage[most]{tcolorbox}
|
||||
\usepackage{qrcode}
|
||||
\IfFileExists{references.bib}{%
|
||||
\usepackage[backend=biber,style=numeric,sorting=none]{biblatex}%
|
||||
\addbibresource{references.bib}%
|
||||
}{}
|
||||
|
||||
\hypersetup{hidelinks}
|
||||
|
||||
\IfFileExists{build-meta.tex}{%
|
||||
\input{build-meta.tex}%
|
||||
}{%
|
||||
\newcommand{\BuildCommit}{lokalna}%
|
||||
}
|
||||
|
||||
\newcommand{\DocumentAuthor}{M. Pabiszczak}
|
||||
\newcommand{\DocumentYear}{2026}
|
||||
\newcommand{\CardSeries}{freertos-cpp}
|
||||
\newcommand{\CardNumber}{06}
|
||||
\newcommand{\CardCount}{16}
|
||||
\newcommand{\CardSlug}{allocator-resource}
|
||||
\newcommand{\CardVersion}{v00.01}
|
||||
\newcommand{\DocumentUUID}{1af48a2e-8564-4e18-8115-b2c0208169d6}
|
||||
|
||||
\newcommand{\EmptyCheck}{\(\square\)}
|
||||
\newcommand{\EscAnswerLines}[1][3]{\par\noindent\dotfill\par\noindent\dotfill\par\noindent\dotfill}
|
||||
\newcommand{\EscWriteRow}[1][2.8em]{\rule{0pt}{#1}}
|
||||
\setlength{\parindent}{0pt}
|
||||
\setlength{\parskip}{0.45em}
|
||||
\setlength{\headheight}{24mm}
|
||||
\setlength{\headsep}{3mm}
|
||||
\setlength{\footskip}{8mm}
|
||||
\setlength{\marginparwidth}{1.5cm}
|
||||
\setlength{\marginparsep}{0.25cm}
|
||||
\renewcommand{\arraystretch}{1.22}
|
||||
% Margin separator lines disabled for layout trial.
|
||||
|
||||
\newcommand{\ESCPageResourceHeader}{%
|
||||
\begingroup%
|
||||
\setlength{\parskip}{0pt}%
|
||||
\setlength{\fboxsep}{0pt}%
|
||||
\setlength{\fboxrule}{0.35pt}%
|
||||
\setlength{\arrayrulewidth}{0.35pt}%
|
||||
\noindent\fbox{%
|
||||
\begin{minipage}[c][23.5mm][c]{\dimexpr\textwidth-2\fboxrule\relax}%
|
||||
\begin{minipage}[c][23mm][c]{\dimexpr\linewidth-24mm-0.35pt\relax}%
|
||||
\setlength{\tabcolsep}{0pt}%
|
||||
\renewcommand{\arraystretch}{0}%
|
||||
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.50000000\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.15126050\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries TITLE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{7.7}{7.9}\selectfont\ttfamily\bfseries MemoryResource i FreeRtosAllocator<T>}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries VER.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.6}{6.8}\selectfont\ttfamily\bfseries v00.01}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries DATETIME}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries 2026-07-19T00:00:00+02:00}\hspace{0.45mm}}\par} \\%
|
||||
\end{tabularx}%
|
||||
\par\nointerlineskip%
|
||||
\hrule height0.35pt%
|
||||
\nointerlineskip%
|
||||
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.50000000\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.25210084\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.14285714\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries PROJECT}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{7.7}{7.9}\selectfont\ttfamily\bfseries Freestanding C++ nad FreeRTOS}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries SERIES}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries FreeRTOS C++}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries CARD}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries 06/16}\hspace{0.45mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{3.2}{3.4}\selectfont\ttfamily\bfseries SHEET}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{6.4}{6.6}\selectfont\ttfamily\bfseries \thepage/\pageref{LastPage}}\hspace{0.45mm}}\par} \\%
|
||||
\end{tabularx}%
|
||||
\par\nointerlineskip%
|
||||
\hrule height0.35pt%
|
||||
\nointerlineskip%
|
||||
\begin{tabularx}{\linewidth}{@{}p{\dimexpr0.05882353\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.05042017\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.14705882\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.09243697\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.07563025\linewidth-\arrayrulewidth\relax}|p{\dimexpr0.07563025\linewidth-\arrayrulewidth\relax}|X@{}}%
|
||||
\parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries SUBJ.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries Inf.}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries PROG.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries CORE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries SCOPE}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries LEVEL}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hspace{0.15mm}{\fontsize{2.85}{3}\selectfont\ttfamily\bfseries POS.}\par\nointerlineskip\vspace{0.12mm}\noindent\makebox[\linewidth][r]{{\fontsize{4.45}{4.65}\selectfont\ttfamily\bfseries —}\hspace{0.15mm}}\par} & \parbox[c][5.19414mm][c]{\linewidth}{\hbox to\linewidth{\hspace{0.35mm}{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries GITEA UUID}\hfill{\fontsize{3.4}{3.75}\selectfont\ttfamily 1af48a2e-8564-4e18-8115-b2c0208169d6}\hspace{0.35mm}}\par\nointerlineskip\hbox to\linewidth{\hspace{0.35mm}{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries CARD UUID} {\fontsize{3.4}{3.75}\selectfont\ttfamily 1af48a2e-8564-4e18-8115-b2c0208169d6}\hfill{\fontsize{3.4}{3.75}\selectfont\ttfamily\bfseries AUTHOR M. Pabiszczak}\hspace{0.35mm}}} \\%
|
||||
\end{tabularx}%
|
||||
\par\nointerlineskip%
|
||||
\hrule height0.35pt%
|
||||
\nointerlineskip%
|
||||
\begin{tabularx}{\linewidth}{@{}X@{}}%
|
||||
\parbox[c][3.46276mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{4.5}{4.85}\selectfont\ttfamily\bfseries GITEA} {\fontsize{4.5}{4.85}\selectfont\ttfamily\href{https://zsl-gitea.mpabi.pl/edu-freertos-cpp/lab-rv32i-freertos-allocator-resource}{\nolinkurl{https://zsl-gitea.mpabi.pl/edu-freertos-cpp/lab-rv32i-freertos-allocator-resource}}}} \\%
|
||||
\end{tabularx}%
|
||||
\par\nointerlineskip%
|
||||
\hrule height0.35pt%
|
||||
\nointerlineskip%
|
||||
\begin{tabularx}{\linewidth}{@{}X@{}}%
|
||||
\parbox[c][3.46276mm][c]{\linewidth}{\hspace{0.45mm}{\fontsize{4.5}{4.85}\selectfont\ttfamily\bfseries HTML} {\fontsize{4.5}{4.85}\selectfont\ttfamily\href{}{\nolinkurl{}}}} \\%
|
||||
\end{tabularx}%
|
||||
\end{minipage}%
|
||||
\vrule width0.35pt%
|
||||
\begin{minipage}[c][23mm][c]{24mm}\centering%
|
||||
{\tiny QR wyłączony}%
|
||||
\end{minipage}%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\endgroup%
|
||||
}
|
||||
|
||||
\pagestyle{fancy}
|
||||
\fancyhf{}
|
||||
\fancyhead[C]{\ESCPageResourceHeader}
|
||||
\renewcommand{\headrulewidth}{0pt}
|
||||
\renewcommand{\footrulewidth}{0pt}
|
||||
|
||||
\newcommand{\ESCMarginTag}[4]{%
|
||||
\hspace*{#1}\textcolor{#2}{#3~#4}%
|
||||
}
|
||||
\newcommand{\ESCSectionBlockStart}{%
|
||||
\Needspace{8\baselineskip}%
|
||||
\par\vspace{0.35em}%
|
||||
\noindent\textcolor{black!28}{\rule{\textwidth}{0.35pt}}%
|
||||
\par\vspace{0.15em}%
|
||||
}
|
||||
\newcommand{\ESCSectionBlockEnd}{%
|
||||
\par\vspace{0.25em}%
|
||||
\noindent\textcolor{black!18}{\rule{\textwidth}{0.25pt}}%
|
||||
\par\vspace{0.45em}%
|
||||
}
|
||||
\newcommand{\ESCTinyStepSeparator}{%
|
||||
\par\vspace{0.10em}%
|
||||
\noindent{\color{black!18}\leaders\hbox{\rule{0.60em}{0.22pt}\hspace{0.38em}}\hfill\kern0pt}%
|
||||
\par\vspace{0.10em}%
|
||||
}
|
||||
\newtcolorbox{ESCTaskFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black,boxsep=0pt,left=1.4mm,right=1.4mm,top=0.8mm,bottom=0.8mm,colbacktitle=black!7,coltitle=black,title={\ttfamily\bfseries TASK\quad #1}}
|
||||
\newtcolorbox{ESCBlockFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black!55,boxsep=0pt,left=1.0mm,right=1.0mm,top=0.6mm,bottom=0.6mm,colbacktitle=black!4,coltitle=black,title={\ttfamily\bfseries BLOCK\quad #1}}
|
||||
\newtcolorbox{ESCStepFrame}[1]{enhanced,breakable,arc=0pt,boxrule=0.35pt,colback=white,colframe=black!28,boxsep=0pt,left=0.8mm,right=0.8mm,top=0.45mm,bottom=0.45mm,colbacktitle=black!2,coltitle=black,title={\ttfamily STEP\quad #1}}
|
||||
\newtcolorbox{ESCConclusionFrame}{enhanced,breakable,arc=0pt,boxrule=0.45pt,colback=blue!2,colframe=blue!45!black,boxsep=0pt,left=1.2mm,right=1.2mm,top=0.7mm,bottom=0.7mm,colbacktitle=blue!7,coltitle=black,title={\ttfamily\bfseries WNIOSEK}}
|
||||
|
||||
\newcommand{\ESCLearningTreeWidget}{%
|
||||
\reversemarginpar
|
||||
\marginnote[%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\begin{minipage}[t]{\marginparwidth}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\hspace*{0.28em}\textcolor{orange!85!black}{EK~LOCAL~MEM.ALLOC}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Bada typed allocation, adresy, alignment i failure state.}}\par%
|
||||
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~MEM.ALLOC}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Pokazuje dwa storage domains, guard przed mnożeniem i niezmienione available/used.}}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
]{}
|
||||
\normalmarginpar
|
||||
|
||||
\marginnote{%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\hspace*{0.06cm}%
|
||||
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\hspace*{0.28em}\textcolor{green!50!black}{EN~LOCAL~EN~MEM.RESOURCE}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Analizuje ownership resource view i politykę release/reset.}}\par%
|
||||
\hspace*{0.54em}\textcolor{blue!70!black}{KW~LOCAL~KW~MEM.RESOURCE}\textcolor{black!48}{\pdftooltip[width=\textwidth]{.01}{Uzasadnia provider lifetime, arena no-op deallocate i zbiorczy reset.}}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
\begin{document}
|
||||
\sloppy
|
||||
|
||||
\vspace{1.0em}
|
||||
\noindent{\Large\bfseries Cel karty}\par
|
||||
\vspace{0.35em}
|
||||
Uczeń używa jednego typed allocatora nad heapem i areną, klasyfikuje adresy oraz dowodzi, że overflow i capacity failure nie zmieniają stanu storage.
|
||||
|
||||
\vspace{0.8em}
|
||||
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
|
||||
\vspace{0.7em}
|
||||
\noindent{\Large\bfseries Zakres karty}\par
|
||||
\vspace{0.35em}
|
||||
Resource ma context i dwa function pointers, bez vtable. Arena jest monotoniczna i resetowana zbiorczo; karta nie deklaruje zgodności z hosted STL ani automatycznego czasu życia obiektów.
|
||||
|
||||
\vspace{0.8em}
|
||||
\noindent\textcolor{black!25}{\rule{\textwidth}{0.35pt}}
|
||||
|
||||
\clearpage
|
||||
|
||||
\ESCSectionBlockStart
|
||||
\section{Resource bez wirtualności}
|
||||
\reversemarginpar
|
||||
\marginnote[%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\begin{minipage}[t]{\marginparwidth}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
]{}[-3.1em]
|
||||
\normalmarginpar
|
||||
|
||||
\marginnote{%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\hspace*{0.06cm}%
|
||||
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
}[-3.1em]
|
||||
|
||||
|
||||
\begingroup
|
||||
\scriptsize\ttfamily\color{black!60}\sloppy
|
||||
\noindent drzewka: \pdftooltip[width=\textwidth]{D1}{K06.WE01.OG.LOCAL.MEM.RESOURCE.01 | WE 01: Resource i typed allocator. Zaprojektowanie\textCR jawnego, fallible i runtime-selected źródła pamięci dla typów. | EN LOCAL MEM.RESOURCE.01:\textCR Analizuje ownership resource view i politykę release/reset. | KW LOCAL MEM.RESOURCE.01:\textCR Uzasadnia provider lifetime, arena no-op deallocate i zbiorczy reset.}\par
|
||||
\vspace{0.10em}%
|
||||
\noindent K1: Zapisz context i dwa różne adresy funkcji allocate.\quad D1\par
|
||||
\ESCTinyStepSeparator
|
||||
\noindent K2: Potwierdź brak vtable/typeinfo w ELF.\quad D1\par
|
||||
\par\vspace{0.18em}%
|
||||
\endgroup
|
||||
|
||||
Narysuj context oraz allocate/deallocate function pointers. Provider posiada stan, a MemoryResource jest nieposiadającym widokiem.
|
||||
\ESCSectionBlockEnd
|
||||
|
||||
\ESCSectionBlockStart
|
||||
\section{Overflow-safe FreeRtosAllocator<T>}
|
||||
\reversemarginpar
|
||||
\marginnote[%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\begin{minipage}[t]{\marginparwidth}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
]{}[-3.1em]
|
||||
\normalmarginpar
|
||||
|
||||
\marginnote{%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\hspace*{0.06cm}%
|
||||
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
}[-3.1em]
|
||||
|
||||
|
||||
\begingroup
|
||||
\scriptsize\ttfamily\color{black!60}\sloppy
|
||||
\noindent drzewka: \pdftooltip[width=\textwidth]{D1}{K06.WE01.TECH.LOCAL.MEM.ALLOC.01 | WE 01: Resource i typed allocator. Zaprojektowanie\textCR jawnego, fallible i runtime-selected źródła pamięci dla typów. | EK LOCAL MEM.ALLOC.01: Bada\textCR typed allocation, adresy, alignment i failure state. | KW LOCAL MEM.ALLOC.01: Pokazuje dwa\textCR storage domains, guard przed mnożeniem i niezmienione available/used.}\par
|
||||
\vspace{0.10em}%
|
||||
\noindent K1: Uzupełnij guard i udowodnij brak wywołania providera przy overflow.\quad D1\par
|
||||
\ESCTinyStepSeparator
|
||||
\noindent K2: Sprawdź niezmienione available/used po capacity failure.\quad D1\par
|
||||
\par\vspace{0.18em}%
|
||||
\endgroup
|
||||
|
||||
Sprawdź count > SIZE_MAX/sizeof(T) przed mnożeniem, potem przekaż bytes i alignof(T) do resource.
|
||||
\ESCSectionBlockEnd
|
||||
|
||||
\ESCSectionBlockStart
|
||||
\section{Heap kontra arena}
|
||||
\reversemarginpar
|
||||
\marginnote[%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\begin{minipage}[t]{\marginparwidth}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{TECH}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
]{}[-3.1em]
|
||||
\normalmarginpar
|
||||
|
||||
\marginnote{%
|
||||
\begin{minipage}{\marginparwidth}%
|
||||
\raggedright
|
||||
{\fontsize{3.55}{3.95}\selectfont\ttfamily
|
||||
\hspace*{0.06cm}%
|
||||
\begin{minipage}[t]{\dimexpr\marginparwidth-0.06cm\relax}%
|
||||
\raggedright
|
||||
{\bfseries\textcolor{black!65}{OG}\par}%
|
||||
\vspace{0.08em}%
|
||||
\hspace*{0.00em}\textcolor{red}{WE~01}\textcolor{black!48}{}\par%
|
||||
\end{minipage}%
|
||||
}%
|
||||
\end{minipage}%
|
||||
}[-3.1em]
|
||||
|
||||
|
||||
\begingroup
|
||||
\scriptsize\ttfamily\color{black!60}\sloppy
|
||||
\noindent drzewka: \pdftooltip[width=\textwidth]{D1}{K06.WE01.TECH.LOCAL.MEM.ALLOC.01 | WE 01: Resource i typed allocator. Zaprojektowanie\textCR jawnego, fallible i runtime-selected źródła pamięci dla typów. | EK LOCAL MEM.ALLOC.01: Bada\textCR typed allocation, adresy, alignment i failure state. | KW LOCAL MEM.ALLOC.01: Pokazuje dwa\textCR storage domains, guard przed mnożeniem i niezmienione available/used.} \pdftooltip[width=\textwidth]{D2}{K06.WE01.OG.LOCAL.MEM.RESOURCE.01 | WE 01: Resource i typed allocator. Zaprojektowanie\textCR jawnego, fallible i runtime-selected źródła pamięci dla typów. | EN LOCAL MEM.RESOURCE.01:\textCR Analizuje ownership resource view i politykę release/reset. | KW LOCAL MEM.RESOURCE.01:\textCR Uzasadnia provider lifetime, arena no-op deallocate i zbiorczy reset.}\par
|
||||
\vspace{0.10em}%
|
||||
\noindent K1: Pokaż heap address w ucHeap i arena address w .bss.\quad D1\par
|
||||
\ESCTinyStepSeparator
|
||||
\noindent K2: Pokaż arena used 64, 64, 0 po allocate/deallocate/reset.\quad D2\par
|
||||
\par\vspace{0.18em}%
|
||||
\endgroup
|
||||
|
||||
Przydziel po 16 uint32_t, sprawdź zakresy i alignment, a następnie porównaj natychmiastowy heap free z arena deallocate i reset.
|
||||
\ESCSectionBlockEnd
|
||||
|
||||
\end{document}
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
\documentclass[10pt]{article}
|
||||
\usepackage[T1]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage[polish]{babel}
|
||||
\usepackage[a4paper,margin=1.55cm]{geometry}
|
||||
\usepackage{array,tabularx,booktabs}
|
||||
\usepackage{amsmath,amssymb}
|
||||
\usepackage{xcolor,listings}
|
||||
\usepackage{hyperref,fancyhdr,lastpage,enumitem}
|
||||
\definecolor{accent}{HTML}{16324A}
|
||||
\definecolor{accentlight}{HTML}{EEF3F7}
|
||||
\definecolor{rulegray}{HTML}{D7DEE5}
|
||||
\hypersetup{colorlinks=true,linkcolor=accent,urlcolor=blue}
|
||||
\IfFileExists{build-meta.tex}{\input{build-meta.tex}}{\newcommand{\BuildCommit}{local}}
|
||||
\newcommand{\PublisherDomain}{mpabi}
|
||||
\newcommand{\CardArea}{inf}
|
||||
\newcommand{\CardSeries}{freertos-cpp}
|
||||
\newcommand{\CardNumber}{06}
|
||||
\newcommand{\CardCount}{16}
|
||||
\newcommand{\CardSlug}{allocator-resource}
|
||||
\newcommand{\CardVersion}{v00.01}
|
||||
\newcommand{\DocumentUUID}{1af48a2e-8564-4e18-8115-b2c0208169d6}
|
||||
\newcommand{\blank}[1]{\rule{#1}{.2pt}}
|
||||
\lstset{language=C++,basicstyle=\ttfamily\scriptsize,columns=fullflexible,
|
||||
keepspaces=true,frame=single,breaklines=true,showstringspaces=false,
|
||||
numbers=none,backgroundcolor=\color{accentlight},rulecolor=\color{rulegray}}
|
||||
\pagestyle{fancy}
|
||||
\fancyhf{}
|
||||
\lhead{\textbf{K06 · FreeRTOS C++ · resource}}
|
||||
\rhead{\small L05 · arena i typed allocator}
|
||||
\lfoot{\scriptsize commit \BuildCommit}
|
||||
\cfoot{\scriptsize \thepage/\pageref{LastPage}}
|
||||
\rfoot{\scriptsize V11.3.0 / \CardVersion}
|
||||
\setlength{\headheight}{14pt}
|
||||
\setlength{\footskip}{19pt}
|
||||
\setlist[itemize]{nosep,leftmargin=1.45em}
|
||||
\setlist[enumerate]{nosep,leftmargin=1.65em}
|
||||
\begin{document}
|
||||
\sloppy
|
||||
|
||||
\begin{center}
|
||||
{\LARGE\bfseries \texttt{MemoryResource} i \texttt{FreeRtosAllocator<T>}}\par
|
||||
\vspace{.25em}
|
||||
{\large wybór storage w runtime bez vtable i bez przepełnienia}\par
|
||||
\end{center}
|
||||
|
||||
\noindent\begin{tabularx}{\textwidth}{@{}p{1.65cm}Xp{1.55cm}X@{}}
|
||||
\toprule
|
||||
Karta & K06 / \CardCount & Czas & 30 minut \\
|
||||
Platforma & Hazard3 / RV32I & Język & freestanding C++17 \\
|
||||
Zasoby & heap + arena 256 B & Dispatch & context + 2 funkcje \\
|
||||
Wersja & \CardVersion & UUID karty & \texttt{1af48a2e-...} \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
\section*{Jedna operacja typowana, dwie polityki}
|
||||
|
||||
Ten sam \texttt{FreeRtosAllocator<uint32\_t>} rezerwuje 16 elementów z:
|
||||
|
||||
\begin{itemize}
|
||||
\item \texttt{HeapResource}: \texttt{pvPortMalloc/vPortFree}, pojedyncze free;
|
||||
\item \texttt{StaticArenaResource}: bufor w \texttt{.bss}, liniowy offset,
|
||||
pojedyncze deallocate nic nie robi, odzyskanie przez \texttt{reset()}.
|
||||
\end{itemize}
|
||||
|
||||
\noindent\fcolorbox{accent}{accentlight}{%
|
||||
\begin{minipage}{.94\textwidth}
|
||||
\textbf{Kontrakt K06.} Przed \texttt{count*sizeof(T)} sprawdzamy dzielenie.
|
||||
Porażka nie zmienia dostępnej pamięci ani offsetu. Widok zasobu nie posiada
|
||||
kontekstu; provider musi żyć dłużej niż allocator.
|
||||
\end{minipage}}
|
||||
|
||||
\section*{Plan 30 minut}
|
||||
\noindent\begin{tabularx}{\textwidth}{@{}p{1.35cm}p{3.1cm}X@{}}
|
||||
\toprule
|
||||
Czas & Tryb & Dowód \\
|
||||
\midrule
|
||||
0--5 & kontrakt & trzy słowa: context, allocate, deallocate \\
|
||||
5--10 & arytmetyka & guard przed mnożeniem count i sizeof(T) \\
|
||||
10--16 & alokacja & 16 uint32 z heapu i 16 z areny \\
|
||||
16--21 & adresy & ucHeap kontra globalny bufor .bss, alignment \\
|
||||
21--26 & failure & overflow i capacity nie zmieniają storage \\
|
||||
26--30 & release & heap free kontra arena deallocate/reset \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
\section*{Predykcja}
|
||||
Rozmiar 16 elementów \texttt{uint32\_t}: \blank{2cm} B. Po arena-deallocate
|
||||
\texttt{used}=\blank{2cm}; po reset \texttt{used}=\blank{2cm}.
|
||||
|
||||
\newpage
|
||||
\section{Resource to wartość, nie hierarchia klas}
|
||||
|
||||
\begin{lstlisting}
|
||||
class MemoryResource final {
|
||||
void* context_;
|
||||
void* (*allocate_)(void*, size_t, size_t) noexcept;
|
||||
void (*deallocate_)(void*, void*, size_t, size_t) noexcept;
|
||||
};
|
||||
\end{lstlisting}
|
||||
|
||||
\begin{center}
|
||||
\texttt{allocator -> MemoryResource -> function(context, bytes, alignment)}
|
||||
\end{center}
|
||||
|
||||
\noindent\begin{tabularx}{\textwidth}{@{}p{4.0cm}p{4.0cm}X@{}}
|
||||
\toprule
|
||||
Element & Właściciel & Co sprawdzamy \\
|
||||
\midrule
|
||||
\texttt{MemoryResource} & nikt; non-owning view & 3 wskaźniki, bez vtable \\
|
||||
\texttt{HeapResource} & liczniki providera & alignment $\le$ gwarancja portu \\
|
||||
\texttt{StaticArenaResource} & bufor przekazany przez caller & base, capacity, used \\
|
||||
\texttt{FreeRtosAllocator<T>} & nikt; kopiuje view & count, overflow, alignof(T) \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
Wpisz odczyty z checkpointu 1:
|
||||
|
||||
\noindent\begin{tabularx}{\textwidth}{@{}p{5.5cm}X@{}}
|
||||
\toprule
|
||||
Pole & Adres \\
|
||||
\midrule
|
||||
heap context & \blank{7cm} \\
|
||||
arena context & \blank{7cm} \\
|
||||
heap allocate function & \blank{7cm} \\
|
||||
arena allocate function & \blank{7cm} \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
Funkcje allocate powinny być różne, ale kod wywołujący typed allocator jest
|
||||
ten sam. Koszt dispatch to jawne pośrednie wywołanie, nie wirtualny obiekt.
|
||||
|
||||
\section{Guard mnożenia przed mnożeniem}
|
||||
|
||||
Uzupełnij:
|
||||
|
||||
\begin{lstlisting}
|
||||
T* try_allocate(size_t count) const noexcept {
|
||||
if (count == 0 || count > ________________________) {
|
||||
return nullptr;
|
||||
}
|
||||
const size_t bytes = _____________________________;
|
||||
return static_cast<T*>(resource.allocate_bytes(
|
||||
bytes, ___________________));
|
||||
}
|
||||
\end{lstlisting}
|
||||
|
||||
\textbf{Dlaczego test po mnożeniu jest błędny?} Gdy wynik zawinie się do małej
|
||||
liczby, allocator może zwrócić za mały, pozornie poprawny blok. W tej karcie
|
||||
overflow nie wywołuje nawet funkcji providera.
|
||||
|
||||
\subsection*{Dwie klasy porażki}
|
||||
|
||||
\begin{enumerate}
|
||||
\item \textbf{Arithmetic rejection}: count nie daje się bezpiecznie
|
||||
przeliczyć; context nie jest dotykany.
|
||||
\item \textbf{Capacity rejection}: liczba bajtów jest poprawna, ale provider
|
||||
nie ma miejsca; zwraca \texttt{nullptr} i nie przesuwa storage.
|
||||
\end{enumerate}
|
||||
|
||||
\newpage
|
||||
\section{Ten sam bufor logiczny, dwa obszary adresowe}
|
||||
|
||||
\begin{center}
|
||||
\texttt{HeapResource -> [16 * uint32] inside ucHeap}\\[.5em]
|
||||
\texttt{ArenaResource -> [16 * uint32] inside g\_static\_arena\_storage}
|
||||
\end{center}
|
||||
|
||||
\noindent\begin{tabularx}{\textwidth}{@{}p{4.7cm}p{3.4cm}X@{}}
|
||||
\toprule
|
||||
Dowód & Heap & Arena \\
|
||||
\midrule
|
||||
adres & \blank{3cm} & \blank{3cm} \\
|
||||
zakres pamięci & \blank{3cm} & \blank{3cm} \\
|
||||
\texttt{address \% alignof(uint32\_t)} & \blank{3cm} & \blank{3cm} \\
|
||||
liczba elementów / bajtów & \blank{3cm} & \blank{3cm} \\
|
||||
suma wpisanych wartości & \blank{3cm} & \blank{3cm} \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
\section*{Porażka ma zostawić stan storage bez zmian}
|
||||
|
||||
\begin{tabularx}{\textwidth}{@{}p{5.2cm}p{4.0cm}X@{}}
|
||||
\toprule
|
||||
Próba & Stan przed & Stan po \\
|
||||
\midrule
|
||||
overflow count dla heap allocator & free=\blank{2cm} & free=\blank{2cm} \\
|
||||
overflow count dla arena allocator & used=\blank{2cm} & used=\blank{2cm} \\
|
||||
za duży poprawny byte request heap & free=\blank{2cm} & free=\blank{2cm} \\
|
||||
za duży poprawny byte request arena & used=\blank{2cm} & used=\blank{2cm} \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
Przy overflow provider failure count też się nie zmienia, bo wywołanie zostaje
|
||||
odrzucone wcześniej. Przy capacity failure może wzrosnąć licznik nieudanych
|
||||
\emph{prób}, ale ilość zajętej pamięci pozostaje taka sama.
|
||||
|
||||
\section*{Dwie polityki zwalniania}
|
||||
|
||||
\begin{center}
|
||||
\begin{tabular}{c|c|c|c}
|
||||
& po allocate & po deallocate & po reset \\
|
||||
\hline
|
||||
heap current used & 64+metadata & 0 względem baseline & nie dotyczy \\
|
||||
arena used & 64 & \textbf{64} & \textbf{0}
|
||||
\end{tabular}
|
||||
\end{center}
|
||||
|
||||
Dlaczego arena nie może zwolnić „środkowego” bloku przez samo cofnięcie
|
||||
offsetu? \blank{13cm}\\[1em]\blank{16cm}
|
||||
|
||||
Reset jest operacją zbiorczą. Wolno go wykonać dopiero, gdy żaden żywy obiekt
|
||||
nie używa adresów z areny.
|
||||
|
||||
\newpage
|
||||
\section{Hazard3/GDB: siedem checkpointów}
|
||||
|
||||
\begin{lstlisting}[language=bash]
|
||||
make check
|
||||
riscv64-unknown-elf-gdb build/task01_allocator_resource/prog.elf
|
||||
b allocator_resource_debug_checkpoint
|
||||
\end{lstlisting}
|
||||
|
||||
\noindent\begin{tabularx}{\textwidth}{@{}p{1cm}p{4.0cm}X@{}}
|
||||
\toprule
|
||||
STOP & Stan & Wymagany dowód \\
|
||||
\midrule
|
||||
1 & dwa resource views & context i function pointers zapisane \\
|
||||
2 & dwa bufory żywe & heap w ucHeap, arena w .bss, alignment i sumy \\
|
||||
3 & overflow rejected & oba nullptr; provider/storage bez zmian \\
|
||||
4 & capacity rejected & oba nullptr; available/used bez zmian \\
|
||||
5 & heap deallocated & heap current free wraca do baseline \\
|
||||
6 & arena deallocated & arena used nadal 64 \\
|
||||
7 & arena reset & arena used 0; liczniki i pass=1 \\
|
||||
\bottomrule
|
||||
\end{tabularx}
|
||||
|
||||
\subsection*{Odczyty}
|
||||
\begin{lstlisting}
|
||||
p/x g_heap_address
|
||||
p/x g_arena_address
|
||||
p/x g_arena_storage_begin
|
||||
p/x g_arena_storage_end
|
||||
p g_heap_after_capacity_failure
|
||||
p g_arena_used_after_allocation
|
||||
p g_arena_used_after_deallocate
|
||||
p g_arena_used_after_reset
|
||||
p g_heap_successful_resource_allocations
|
||||
p g_heap_failed_resource_allocations
|
||||
p g_arena_successful_resource_allocations
|
||||
p g_arena_failed_resource_allocations
|
||||
p g_allocator_resource_pass
|
||||
\end{lstlisting}
|
||||
|
||||
\section*{Zaliczenie}
|
||||
\begin{itemize}
|
||||
\item $\square$ rysuję 3-słowowy resource i nie nazywam go vtable;
|
||||
\item $\square$ stosuję \texttt{count > SIZE\_MAX/sizeof(T)} przed mnożeniem;
|
||||
\item $\square$ klasyfikuję heap address i arena address po zakresach;
|
||||
\item $\square$ pokazuję niezmieniony storage po obu rodzajach failure;
|
||||
\item $\square$ odróżniam heap free, arena deallocate i arena reset;
|
||||
\item $\square$ wiem, że view nie posiada providera ani bufora.
|
||||
\end{itemize}
|
||||
|
||||
\section*{Wyjście}
|
||||
Dlaczego \texttt{reset()} nie może być ukryty w destruktorze dowolnego
|
||||
allocatora? \blank{12cm}\\[1em]
|
||||
Co musiałoby żyć dłużej: \texttt{FreeRtosAllocator<T>} czy jego provider?
|
||||
\\[.4em]\blank{8cm}
|
||||
|
||||
\vfill
|
||||
\noindent\textbf{Następna karta K07:} posiadający obiekt taska, jawne
|
||||
\texttt{start()}, stabilny kontekst i statyczny trampoline C $\rightarrow$ C++.
|
||||
\end{document}
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user