Skip to content

Commit a554c14

Browse files
committed
add coverage report generation in CI
adds the coverage report generation in CI . Also make the coverage script more CI friendly.
1 parent 10679bb commit a554c14

File tree

100 files changed

+454
-2
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

100 files changed

+454
-2
lines changed

ci/ci-tests.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,3 +137,28 @@ RUSTFLAGS="--cfg=splicing" cargo test --verbose --color always -p lightning
137137
RUSTFLAGS="--cfg=async_payments" cargo test --verbose --color always -p lightning
138138
[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
139139
RUSTFLAGS="--cfg=lsps1_service" cargo test --verbose --color always -p lightning-liquidity
140+
141+
# Generate fuzz coverage report
142+
echo -e "\n\nGenerating fuzz coverage report"
143+
if [ -n "$CI" ]; then
144+
# In CI, store coverage in a specific directory for artifact collection
145+
COVERAGE_DIR="coverage-report"
146+
mkdir -p "$COVERAGE_DIR"
147+
echo "Installing cargo-llvm-cov..."
148+
# Install cargo-llvm-cov if not already installed
149+
cargo install cargo-llvm-cov --locked
150+
echo "Running fuzz coverage generation..."
151+
./contrib/generate_fuzz_coverage.sh --output-dir "$COVERAGE_DIR"
152+
echo "Coverage generation completed. Checking results..."
153+
if [ -f "fuzz/$COVERAGE_DIR/html/index.html" ]; then
154+
echo "✓ Coverage report successfully generated at fuzz/$COVERAGE_DIR/html/index.html"
155+
ls -la "fuzz/$COVERAGE_DIR/html/"
156+
else
157+
echo "✗ Coverage report not found at expected location"
158+
echo "Checking what was created in fuzz/$COVERAGE_DIR:"
159+
ls -la "fuzz/$COVERAGE_DIR" || echo "Directory does not exist"
160+
fi
161+
else
162+
# Local development
163+
./contrib/generate_fuzz_coverage.sh
164+
fi

contrib/generate_fuzz_coverage.sh

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22
set -e
33
set -x
44

5+
# Parse command line arguments
6+
OUTPUT_DIR="target/llvm-cov/html"
7+
while [[ $# -gt 0 ]]; do
8+
case $1 in
9+
--output-dir)
10+
OUTPUT_DIR="$2"
11+
shift 2
12+
;;
13+
*)
14+
echo "Unknown option: $1"
15+
echo "Usage: $0 [--output-dir OUTPUT_DIRECTORY]"
16+
exit 1
17+
;;
18+
esac
19+
done
20+
521
# Check if we're in the root directory, if so change to fuzz
622
if [ -d "fuzz" ]; then
723
cd fuzz
@@ -32,11 +48,28 @@ if [ "$show_corpus_message" = true ]; then
3248
echo ""
3349
fi
3450

51+
# Create output directory if it doesn't exist
52+
mkdir -p "$OUTPUT_DIR"
53+
3554
export RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz"
3655
# ignore anything in fuzz directory since we don't want coverage of targets
37-
cargo llvm-cov --html --ignore-filename-regex "fuzz/"
56+
cargo llvm-cov --html --ignore-filename-regex "fuzz/" --output-dir "$OUTPUT_DIR"
57+
58+
# Check if coverage report was generated successfully
59+
# The report is generated directly in $OUTPUT_DIR/html/index.html
60+
if [ ! -f "$OUTPUT_DIR/html/index.html" ]; then
61+
echo "Error: Failed to generate coverage report at $OUTPUT_DIR/html/index.html"
62+
# Debug: list what was actually created
63+
echo "Contents of $OUTPUT_DIR:"
64+
ls -la "$OUTPUT_DIR" || echo "Directory $OUTPUT_DIR does not exist"
65+
if [ -d "$OUTPUT_DIR/html" ]; then
66+
echo "Contents of $OUTPUT_DIR/html:"
67+
ls -la "$OUTPUT_DIR/html"
68+
fi
69+
exit 1
70+
fi
3871

3972
echo ""
40-
echo "Coverage report generated in target/llvm-cov/html/index.html"
73+
echo "Coverage report generated in $OUTPUT_DIR/html/index.html"
4174

4275

fuzz/test-coverage/html/control.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
2+
3+
function next_uncovered(selector, reverse, scroll_selector) {
4+
function visit_element(element) {
5+
element.classList.add("seen");
6+
element.classList.add("selected");
7+
8+
if (!scroll_selector) {
9+
scroll_selector = "tr:has(.selected) td.line-number"
10+
}
11+
12+
const scroll_to = document.querySelector(scroll_selector);
13+
if (scroll_to) {
14+
scroll_to.scrollIntoView({behavior: "smooth", block: "center", inline: "end"});
15+
}
16+
17+
}
18+
19+
function select_one() {
20+
if (!reverse) {
21+
const previously_selected = document.querySelector(".selected");
22+
23+
if (previously_selected) {
24+
previously_selected.classList.remove("selected");
25+
}
26+
27+
return document.querySelector(selector + ":not(.seen)");
28+
} else {
29+
const previously_selected = document.querySelector(".selected");
30+
31+
if (previously_selected) {
32+
previously_selected.classList.remove("selected");
33+
previously_selected.classList.remove("seen");
34+
}
35+
36+
const nodes = document.querySelectorAll(selector + ".seen");
37+
if (nodes) {
38+
const last = nodes[nodes.length - 1]; // last
39+
return last;
40+
} else {
41+
return undefined;
42+
}
43+
}
44+
}
45+
46+
function reset_all() {
47+
if (!reverse) {
48+
const all_seen = document.querySelectorAll(selector + ".seen");
49+
50+
if (all_seen) {
51+
all_seen.forEach(e => e.classList.remove("seen"));
52+
}
53+
} else {
54+
const all_seen = document.querySelectorAll(selector + ":not(.seen)");
55+
56+
if (all_seen) {
57+
all_seen.forEach(e => e.classList.add("seen"));
58+
}
59+
}
60+
61+
}
62+
63+
const uncovered = select_one();
64+
65+
if (uncovered) {
66+
visit_element(uncovered);
67+
} else {
68+
reset_all();
69+
70+
71+
const uncovered = select_one();
72+
73+
if (uncovered) {
74+
visit_element(uncovered);
75+
}
76+
}
77+
}
78+
79+
function next_line(reverse) {
80+
next_uncovered("td.uncovered-line", reverse)
81+
}
82+
83+
function next_region(reverse) {
84+
next_uncovered("span.red.region", reverse);
85+
}
86+
87+
function next_branch(reverse) {
88+
next_uncovered("span.red.branch", reverse);
89+
}
90+
91+
document.addEventListener("keypress", function(event) {
92+
console.log(event);
93+
const reverse = event.shiftKey;
94+
if (event.code == "KeyL") {
95+
next_line(reverse);
96+
}
97+
if (event.code == "KeyB") {
98+
next_branch(reverse);
99+
}
100+
if (event.code == "KeyR") {
101+
next_region(reverse);
102+
}
103+
104+
});

fuzz/test-coverage/html/coverage/Users/prabhatverma/projects/rust-lightning/lightning-invoice/src/de.rs.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

fuzz/test-coverage/html/coverage/Users/prabhatverma/projects/rust-lightning/lightning-invoice/src/lib.rs.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

fuzz/test-coverage/html/coverage/Users/prabhatverma/projects/rust-lightning/lightning-invoice/src/ser.rs.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

fuzz/test-coverage/html/coverage/Users/prabhatverma/projects/rust-lightning/lightning-rapid-gossip-sync/src/lib.rs.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

fuzz/test-coverage/html/coverage/Users/prabhatverma/projects/rust-lightning/lightning-rapid-gossip-sync/src/processing.rs.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

fuzz/test-coverage/html/coverage/Users/prabhatverma/projects/rust-lightning/lightning-types/src/features.rs.html

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!doctype html><html><head><meta name='viewport' content='width=device-width,initial-scale=1'><meta charset='UTF-8'><link rel='stylesheet' type='text/css' href='../../../../../../../style.css'><script src='../../../../../../../control.js'></script></head><body><h2>Coverage Report</h2><h4>Created: 2025-06-15 21:30</h4><span class='control'><a href='javascript:next_line()'>next uncovered line (L)</a>, <a href='javascript:next_region()'>next uncovered region (R)</a>, <a href='javascript:next_branch()'>next uncovered branch (B)</a></span><div class='centered'><table><div class='source-name-title'><pre>/Users/prabhatverma/projects/rust-lightning/lightning-types/src/payment.rs</pre></div><tr><td><pre>Line</pre></td><td><pre>Count</pre></td><td><pre>Source</pre></td></tr><tr><td class='line-number'><a name='L1' href='#L1'><pre>1</pre></a></td><td class='skipped-line'></td><td class='code'><pre>// This file is Copyright its original authors, visible in version control</pre></td></tr><tr><td class='line-number'><a name='L2' href='#L2'><pre>2</pre></a></td><td class='skipped-line'></td><td class='code'><pre>// history.</pre></td></tr><tr><td class='line-number'><a name='L3' href='#L3'><pre>3</pre></a></td><td class='skipped-line'></td><td class='code'><pre>//</pre></td></tr><tr><td class='line-number'><a name='L4' href='#L4'><pre>4</pre></a></td><td class='skipped-line'></td><td class='code'><pre>// This file is licensed under the Apache License, Version 2.0 &lt;LICENSE-APACHE</pre></td></tr><tr><td class='line-number'><a name='L5' href='#L5'><pre>5</pre></a></td><td class='skipped-line'></td><td class='code'><pre>// or http://www.apache.org/licenses/LICENSE-2.0&gt; or the MIT license</pre></td></tr><tr><td class='line-number'><a name='L6' href='#L6'><pre>6</pre></a></td><td class='skipped-line'></td><td class='code'><pre>// &lt;LICENSE-MIT or http://opensource.org/licenses/MIT&gt;, at your option.</pre></td></tr><tr><td class='line-number'><a name='L7' href='#L7'><pre>7</pre></a></td><td class='skipped-line'></td><td class='code'><pre>// You may not use this file except in accordance with one or both of these</pre></td></tr><tr><td class='line-number'><a name='L8' href='#L8'><pre>8</pre></a></td><td class='skipped-line'></td><td class='code'><pre>// licenses.</pre></td></tr><tr><td class='line-number'><a name='L9' href='#L9'><pre>9</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L10' href='#L10'><pre>10</pre></a></td><td class='skipped-line'></td><td class='code'><pre>//! Types which describe payments in lightning.</pre></td></tr><tr><td class='line-number'><a name='L11' href='#L11'><pre>11</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L12' href='#L12'><pre>12</pre></a></td><td class='skipped-line'></td><td class='code'><pre>use core::borrow::Borrow;</pre></td></tr><tr><td class='line-number'><a name='L13' href='#L13'><pre>13</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L14' href='#L14'><pre>14</pre></a></td><td class='skipped-line'></td><td class='code'><pre>use bitcoin::hashes::{sha256::Hash as Sha256, Hash as _};</pre></td></tr><tr><td class='line-number'><a name='L15' href='#L15'><pre>15</pre></a></td><td class='skipped-line'></td><td class='code'><pre>use bitcoin::hex::display::impl_fmt_traits;</pre></td></tr><tr><td class='line-number'><a name='L16' href='#L16'><pre>16</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L17' href='#L17'><pre>17</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// The payment hash is the hash of the [`PaymentPreimage`] which is the value used to lock funds</pre></td></tr><tr><td class='line-number'><a name='L18' href='#L18'><pre>18</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// in HTLCs while they transit the lightning network.</pre></td></tr><tr><td class='line-number'><a name='L19' href='#L19'><pre>19</pre></a></td><td class='skipped-line'></td><td class='code'><pre>///</pre></td></tr><tr><td class='line-number'><a name='L20' href='#L20'><pre>20</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// This is not exported to bindings users as we just use [u8; 32] directly</pre></td></tr><tr><td class='line-number'><a name='L21' href='#L21'><pre>21</pre></a></td><td class='skipped-line'></td><td class='code'><pre>#[derive(Hash, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]</pre></td></tr><tr><td class='line-number'><a name='L22' href='#L22'><pre>22</pre></a></td><td class='skipped-line'></td><td class='code'><pre>pub struct PaymentHash(pub [u8; 32]);</pre></td></tr><tr><td class='line-number'><a name='L23' href='#L23'><pre>23</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L24' href='#L24'><pre>24</pre></a></td><td class='skipped-line'></td><td class='code'><pre>impl Borrow&lt;[u8]&gt; for PaymentHash {</pre></td></tr><tr><td class='line-number'><a name='L25' href='#L25'><pre>25</pre></a></td><td class='covered-line'><pre>164</pre></td><td class='code'><pre> fn borrow(&amp;self) -&gt; &amp;[u8] {</pre></td></tr><tr><td class='line-number'><a name='L26' href='#L26'><pre>26</pre></a></td><td class='covered-line'><pre>164</pre></td><td class='code'><pre> &amp;self.0[..]</pre></td></tr><tr><td class='line-number'><a name='L27' href='#L27'><pre>27</pre></a></td><td class='covered-line'><pre>164</pre></td><td class='code'><pre> }</pre></td></tr><tr><td class='line-number'><a name='L28' href='#L28'><pre>28</pre></a></td><td class='skipped-line'></td><td class='code'><pre>}</pre></td></tr><tr><td class='line-number'><a name='L29' href='#L29'><pre>29</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L30' href='#L30'><pre>30</pre></a></td><td class='skipped-line'></td><td class='code'><pre>impl_fmt_traits! {</pre></td></tr><tr><td class='line-number'><a name='L31' href='#L31'><pre>31</pre></a></td><td class='skipped-line'></td><td class='code'><pre> impl fmt_traits for PaymentHash {</pre></td></tr><tr><td class='line-number'><a name='L32' href='#L32'><pre>32</pre></a></td><td class='skipped-line'></td><td class='code'><pre> const LENGTH: usize = 32;</pre></td></tr><tr><td class='line-number'><a name='L33' href='#L33'><pre>33</pre></a></td><td class='skipped-line'></td><td class='code'><pre> }</pre></td></tr><tr><td class='line-number'><a name='L34' href='#L34'><pre>34</pre></a></td><td class='skipped-line'></td><td class='code'><pre>}</pre></td></tr><tr><td class='line-number'><a name='L35' href='#L35'><pre>35</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L36' href='#L36'><pre>36</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// The payment preimage is the &quot;secret key&quot; which is used to claim the funds of an HTLC on-chain</pre></td></tr><tr><td class='line-number'><a name='L37' href='#L37'><pre>37</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// or in a lightning channel.</pre></td></tr><tr><td class='line-number'><a name='L38' href='#L38'><pre>38</pre></a></td><td class='skipped-line'></td><td class='code'><pre>///</pre></td></tr><tr><td class='line-number'><a name='L39' href='#L39'><pre>39</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// This is not exported to bindings users as we just use [u8; 32] directly</pre></td></tr><tr><td class='line-number'><a name='L40' href='#L40'><pre>40</pre></a></td><td class='skipped-line'></td><td class='code'><pre>#[derive(Hash, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]</pre></td></tr><tr><td class='line-number'><a name='L41' href='#L41'><pre>41</pre></a></td><td class='skipped-line'></td><td class='code'><pre>pub struct PaymentPreimage(pub [u8; 32]);</pre></td></tr><tr><td class='line-number'><a name='L42' href='#L42'><pre>42</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L43' href='#L43'><pre>43</pre></a></td><td class='skipped-line'></td><td class='code'><pre>impl Borrow&lt;[u8]&gt; for PaymentPreimage {</pre></td></tr><tr><td class='line-number'><a name='L44' href='#L44'><pre>44</pre></a></td><td class='covered-line'><pre>6</pre></td><td class='code'><pre> fn borrow(&amp;self) -&gt; &amp;[u8] {</pre></td></tr><tr><td class='line-number'><a name='L45' href='#L45'><pre>45</pre></a></td><td class='covered-line'><pre>6</pre></td><td class='code'><pre> &amp;self.0[..]</pre></td></tr><tr><td class='line-number'><a name='L46' href='#L46'><pre>46</pre></a></td><td class='covered-line'><pre>6</pre></td><td class='code'><pre> }</pre></td></tr><tr><td class='line-number'><a name='L47' href='#L47'><pre>47</pre></a></td><td class='skipped-line'></td><td class='code'><pre>}</pre></td></tr><tr><td class='line-number'><a name='L48' href='#L48'><pre>48</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L49' href='#L49'><pre>49</pre></a></td><td class='skipped-line'></td><td class='code'><pre>impl_fmt_traits! {</pre></td></tr><tr><td class='line-number'><a name='L50' href='#L50'><pre>50</pre></a></td><td class='skipped-line'></td><td class='code'><pre> impl fmt_traits for PaymentPreimage {</pre></td></tr><tr><td class='line-number'><a name='L51' href='#L51'><pre>51</pre></a></td><td class='skipped-line'></td><td class='code'><pre> const LENGTH: usize = 32;</pre></td></tr><tr><td class='line-number'><a name='L52' href='#L52'><pre>52</pre></a></td><td class='skipped-line'></td><td class='code'><pre> }</pre></td></tr><tr><td class='line-number'><a name='L53' href='#L53'><pre>53</pre></a></td><td class='skipped-line'></td><td class='code'><pre>}</pre></td></tr><tr><td class='line-number'><a name='L54' href='#L54'><pre>54</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L55' href='#L55'><pre>55</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// Converts a `PaymentPreimage` into a `PaymentHash` by hashing the preimage with SHA256.</pre></td></tr><tr><td class='line-number'><a name='L56' href='#L56'><pre>56</pre></a></td><td class='skipped-line'></td><td class='code'><pre>impl From&lt;PaymentPreimage&gt; for PaymentHash {</pre></td></tr><tr><td class='line-number'><a name='L57' href='#L57'><pre>57</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre> <span class='region red'>fn from(value: PaymentPreimage) -&gt; Self {</span></pre></td></tr><tr><td class='line-number'><a name='L58' href='#L58'><pre>58</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre><span class='region red'> PaymentHash(Sha256::hash(&amp;value.0).to_byte_array())</span></pre></td></tr><tr><td class='line-number'><a name='L59' href='#L59'><pre>59</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre><span class='region red'> }</span></pre></td></tr><tr><td class='line-number'><a name='L60' href='#L60'><pre>60</pre></a></td><td class='skipped-line'></td><td class='code'><pre>}</pre></td></tr><tr><td class='line-number'><a name='L61' href='#L61'><pre>61</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L62' href='#L62'><pre>62</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// The payment secret is used to authenticate the sender of an HTLC to the recipient and tie</pre></td></tr><tr><td class='line-number'><a name='L63' href='#L63'><pre>63</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// multi-part HTLCs together into a single payment.</pre></td></tr><tr><td class='line-number'><a name='L64' href='#L64'><pre>64</pre></a></td><td class='skipped-line'></td><td class='code'><pre>///</pre></td></tr><tr><td class='line-number'><a name='L65' href='#L65'><pre>65</pre></a></td><td class='skipped-line'></td><td class='code'><pre>/// This is not exported to bindings users as we just use [u8; 32] directly</pre></td></tr><tr><td class='line-number'><a name='L66' href='#L66'><pre>66</pre></a></td><td class='skipped-line'></td><td class='code'><pre>#[derive(Hash, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)]</pre></td></tr><tr><td class='line-number'><a name='L67' href='#L67'><pre>67</pre></a></td><td class='skipped-line'></td><td class='code'><pre>pub struct PaymentSecret(pub [u8; 32]);</pre></td></tr><tr><td class='line-number'><a name='L68' href='#L68'><pre>68</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L69' href='#L69'><pre>69</pre></a></td><td class='skipped-line'></td><td class='code'><pre>impl Borrow&lt;[u8]&gt; for PaymentSecret {</pre></td></tr><tr><td class='line-number'><a name='L70' href='#L70'><pre>70</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre> <span class='region red'>fn borrow(&amp;self) -&gt; &amp;[u8] {</span></pre></td></tr><tr><td class='line-number'><a name='L71' href='#L71'><pre>71</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre><span class='region red'> &amp;self.0[..]</span></pre></td></tr><tr><td class='line-number'><a name='L72' href='#L72'><pre>72</pre></a></td><td class='uncovered-line'><pre>0</pre></td><td class='code'><pre><span class='region red'> }</span></pre></td></tr><tr><td class='line-number'><a name='L73' href='#L73'><pre>73</pre></a></td><td class='skipped-line'></td><td class='code'><pre>}</pre></td></tr><tr><td class='line-number'><a name='L74' href='#L74'><pre>74</pre></a></td><td class='skipped-line'></td><td class='code'><pre></pre></td></tr><tr><td class='line-number'><a name='L75' href='#L75'><pre>75</pre></a></td><td class='skipped-line'></td><td class='code'><pre>impl_fmt_traits! {</pre></td></tr><tr><td class='line-number'><a name='L76' href='#L76'><pre>76</pre></a></td><td class='skipped-line'></td><td class='code'><pre> impl fmt_traits for PaymentSecret {</pre></td></tr><tr><td class='line-number'><a name='L77' href='#L77'><pre>77</pre></a></td><td class='skipped-line'></td><td class='code'><pre> const LENGTH: usize = 32;</pre></td></tr><tr><td class='line-number'><a name='L78' href='#L78'><pre>78</pre></a></td><td class='skipped-line'></td><td class='code'><pre> }</pre></td></tr><tr><td class='line-number'><a name='L79' href='#L79'><pre>79</pre></a></td><td class='skipped-line'></td><td class='code'><pre>}</pre></td></tr></table></div></body></html>

0 commit comments

Comments
 (0)