code wiki / (root) / nx_sam_test.nx

nx_sam_test.nx source

↩ module page · 519 lines · 24626 B

1// nx_sam_test.nx -- KAT for SAM v1.4.6 single-line writer. 2// 3// expect_exit: 0 4// 5// license_tier: ORIGINAL 6 7import "nx_syscalls.nx" 8import "nx_const.nx" 9import "nx_sam.nx" 10 11func main() -> i64 { 12 13 // ============================================================ 14 // Section A -- nx_emit_signed_int. 15 // ============================================================ 16 17 let buf: *u8 = sys_mmap(64) 18 19 let n1: i64 = nx_emit_signed_int(42, buf, 0, 64) 20 if n1 != 2 { return 1 } 21 if (buf[0] & 0xff) != 0x34 { return 2 } // '4' 22 if (buf[1] & 0xff) != 0x32 { return 3 } // '2' 23 24 let n2: i64 = nx_emit_signed_int(-300, buf, 0, 64) 25 if n2 != 4 { return 4 } 26 if (buf[0] & 0xff) != 0x2D { return 5 } // '-' 27 if (buf[1] & 0xff) != 0x33 { return 6 } // '3' 28 if (buf[2] & 0xff) != 0x30 { return 7 } // '0' 29 if (buf[3] & 0xff) != 0x30 { return 8 } // '0' 30 31 let n3: i64 = nx_emit_signed_int(0, buf, 0, 64) 32 if n3 != 1 { return 9 } 33 if (buf[0] & 0xff) != 0x30 { return 10 } 34 35 // ============================================================ 36 // Section B -- canonical SAM line. 37 // 38 // QNAME="r1", FLAG=0, RNAME="chr1", POS=12345, MAPQ=60, 39 // CIGAR="3M", RNEXT="*", PNEXT=0, TLEN=0, SEQ="ACG", QUAL="III" 40 // 41 // Expected: "r1\t0\tchr1\t12345\t60\t3M\t*\t0\t0\tACG\tIII\n" 42 // Length: r1(2) + \t + 0(1) + \t + chr1(4) + \t + 12345(5) + \t 43 // + 60(2) + \t + 3M(2) + \t + *(1) + \t + 0(1) + \t + 0(1) 44 // + \t + ACG(3) + \t + III(3) + \n 45 // = 2+1+1+1+4+1+5+1+2+1+2+1+1+1+1+1+1+1+3+1+3+1 = 34 46 // ============================================================ 47 48 // Build concat'd str_buf: "r1" + "chr1" + "3M" + "ACG" + "III" 49 // = 2 + 4 + 2 + 3 + 3 = 14 bytes 50 let str_buf: *u8 = sys_mmap(64) 51 str_buf[0] = 0x72; str_buf[1] = 0x31 // r1 52 str_buf[2] = 0x63; str_buf[3] = 0x68; str_buf[4] = 0x72; str_buf[5] = 0x31 // chr1 53 str_buf[6] = 0x33; str_buf[7] = 0x4D // 3M 54 str_buf[8] = 0x41; str_buf[9] = 0x43; str_buf[10] = 0x47 // ACG 55 str_buf[11] = 0x49; str_buf[12] = 0x49; str_buf[13] = 0x49 // III (Phred Q=40) 56 57 let str_lens: *i64 = sys_mmap(64) as *i64 58 str_lens[NX_SAM_STR_QNAME] = 2 59 str_lens[NX_SAM_STR_RNAME] = 4 60 str_lens[NX_SAM_STR_CIGAR] = 2 61 str_lens[NX_SAM_STR_SEQ] = 3 62 63 let fields: *i64 = sys_mmap(64) as *i64 64 fields[NX_SAM_FIELD_FLAG] = 0 65 fields[NX_SAM_FIELD_POS] = 12345 66 fields[NX_SAM_FIELD_MAPQ] = 60 67 fields[NX_SAM_FIELD_TLEN] = 0 68 69 let out_buf: *u8 = sys_mmap(128) 70 let n: i64 = sam_write_line(str_buf, str_lens, fields, out_buf, 128) 71 if n != 36 { return n + 100 } 72 73 // Byte-by-byte layout (36 bytes, indices 0..35): 74 // 0:r 1:1 2:\t 3:0 4:\t 5:c 6:h 7:r 8:1 9:\t 75 // 10:1 11:2 12:3 13:4 14:5 15:\t 16:6 17:0 18:\t 19:3 76 // 20:M 21:\t 22:* 23:\t 24:0 25:\t 26:0 27:\t 28:A 29:C 77 // 30:G 31:\t 32:I 33:I 34:I 35:\n 78 79 if (out_buf[0] & 0xff) != 0x72 { return 50 } // r 80 if (out_buf[1] & 0xff) != 0x31 { return 51 } // 1 81 if (out_buf[2] & 0xff) != 0x09 { return 52 } // \t 82 if (out_buf[3] & 0xff) != 0x30 { return 53 } // FLAG '0' 83 if (out_buf[4] & 0xff) != 0x09 { return 54 } 84 if (out_buf[5] & 0xff) != 0x63 { return 55 } // c of chr1 85 if (out_buf[8] & 0xff) != 0x31 { return 56 } // 1 of chr1 86 if (out_buf[9] & 0xff) != 0x09 { return 57 } 87 if (out_buf[14] & 0xff) != 0x35 { return 58 } // '5' end of 12345 88 if (out_buf[16] & 0xff) != 0x36 { return 59 } // MAPQ '6' 89 if (out_buf[19] & 0xff) != 0x33 { return 60 } // CIGAR '3' 90 if (out_buf[20] & 0xff) != 0x4D { return 61 } // CIGAR 'M' 91 if (out_buf[22] & 0xff) != 0x2A { return 62 } // RNEXT '*' 92 if (out_buf[24] & 0xff) != 0x30 { return 63 } // PNEXT '0' 93 if (out_buf[26] & 0xff) != 0x30 { return 64 } // TLEN '0' 94 if (out_buf[28] & 0xff) != 0x41 { return 65 } // SEQ 'A' 95 if (out_buf[30] & 0xff) != 0x47 { return 66 } // SEQ 'G' 96 if (out_buf[32] & 0xff) != 0x49 { return 67 } // QUAL 'I' 97 if (out_buf[34] & 0xff) != 0x49 { return 68 } // QUAL 'I' last 98 if (out_buf[35] & 0xff) != 0x0A { return 69 } // \n 99 100 // ============================================================ 101 // Section C -- negative TLEN (paired-end mate downstream). 102 // FLAG=99, RNAME chr1, POS=200, MAPQ=60, CIGAR=3M, TLEN=-150, 103 // SEQ=ACG, QUAL=III. 104 // Expected: tlen field is "-150" (4 bytes). 105 // ============================================================ 106 107 fields[NX_SAM_FIELD_FLAG] = 99 108 fields[NX_SAM_FIELD_POS] = 200 109 fields[NX_SAM_FIELD_TLEN] = 0 - 150 110 111 let n_neg: i64 = sam_write_line(str_buf, str_lens, fields, out_buf, 128) 112 if n_neg < 0 { return 80 } 113 // Delta vs canonical: FLAG "0"->"99" (+1), POS "12345"->"200" (-2), 114 // TLEN "0"->"-150" (+3). Net = +2. Total = 36 + 2 = 38. 115 if n_neg != 38 { return n_neg + 200 } 116 117 // ============================================================ 118 // Section D -- bad inputs. 119 // ============================================================ 120 121 // Reset fields to canonical for next checks. 122 fields[NX_SAM_FIELD_POS] = 12345 123 fields[NX_SAM_FIELD_TLEN] = 0 124 125 // Empty QNAME -> -1. 126 str_lens[NX_SAM_STR_QNAME] = 0 127 if sam_write_line(str_buf, str_lens, fields, out_buf, 128) != -1 { return 90 } 128 str_lens[NX_SAM_STR_QNAME] = 2 129 130 // POS < 1 -> -1. 131 fields[NX_SAM_FIELD_POS] = 0 132 if sam_write_line(str_buf, str_lens, fields, out_buf, 128) != -1 { return 91 } 133 fields[NX_SAM_FIELD_POS] = 12345 134 135 // Capacity overflow -> -1. 136 if sam_write_line(str_buf, str_lens, fields, out_buf, 10) != -1 { return 92 } 137 138 // ============================================================ 139 // Section E -- sam_write_hd_line. Fixed 25-byte output. 140 // ============================================================ 141 142 let hd_buf: *u8 = sys_mmap(64) 143 let n_hd: i64 = sam_write_hd_line(hd_buf, 64) 144 if n_hd != 25 { return n_hd + 300 } 145 146 // Byte-by-byte: 147 // 0:@ 1:H 2:D 3:\t 4:V 5:N 6:: 7:1 8:. 9:6 10:\t 148 // 11:S 12:O 13:: 14:c 15:o 16:o 17:r 18:d 19:i 20:n 21:a 22:t 23:e 24:\n 149 if (hd_buf[0] & 0xff) != 0x40 { return 100 } // @ 150 if (hd_buf[1] & 0xff) != 0x48 { return 101 } // H 151 if (hd_buf[2] & 0xff) != 0x44 { return 102 } // D 152 if (hd_buf[3] & 0xff) != 0x09 { return 103 } // \t 153 if (hd_buf[4] & 0xff) != 0x56 { return 104 } // V 154 if (hd_buf[7] & 0xff) != 0x31 { return 105 } // 1 155 if (hd_buf[9] & 0xff) != 0x36 { return 106 } // 6 156 if (hd_buf[11] & 0xff) != 0x53 { return 107 } // S 157 if (hd_buf[14] & 0xff) != 0x63 { return 108 } // c (coord) 158 if (hd_buf[23] & 0xff) != 0x65 { return 109 } // e (end of coordinate) 159 if (hd_buf[24] & 0xff) != 0x0A { return 110 } // \n 160 161 // Capacity overflow. 162 if sam_write_hd_line(hd_buf, 24) != -1 { return 111 } 163 164 // ============================================================ 165 // Section F -- sam_write_sq_line. 166 // "@SQ\tSN:chr1\tLN:248956422\n" 167 // Length: 3 + 1 + 3 + 4 + 1 + 3 + 9 + 1 = 25 bytes 168 // ============================================================ 169 170 let chr1: *u8 = sys_mmap(8) 171 chr1[0]=0x63; chr1[1]=0x68; chr1[2]=0x72; chr1[3]=0x31 172 173 let sq_buf: *u8 = sys_mmap(64) 174 let n_sq: i64 = sam_write_sq_line(chr1, 4, 248956422, sq_buf, 64) 175 if n_sq != 25 { return n_sq + 400 } 176 177 // 0:@ 1:S 2:Q 3:\t 4:S 5:N 6:: 7:c 8:h 9:r 10:1 11:\t 178 // 12:L 13:N 14:: 15:2 16:4 17:8 18:9 19:5 20:6 21:4 22:2 23:2 24:\n 179 if (sq_buf[0] & 0xff) != 0x40 { return 120 } // @ 180 if (sq_buf[1] & 0xff) != 0x53 { return 121 } // S 181 if (sq_buf[2] & 0xff) != 0x51 { return 122 } // Q 182 if (sq_buf[3] & 0xff) != 0x09 { return 123 } // \t 183 if (sq_buf[4] & 0xff) != 0x53 { return 124 } // S 184 if (sq_buf[6] & 0xff) != 0x3A { return 125 } // : 185 if (sq_buf[7] & 0xff) != 0x63 { return 126 } // c of chr1 186 if (sq_buf[10] & 0xff) != 0x31 { return 127 } // 1 of chr1 187 if (sq_buf[11] & 0xff) != 0x09 { return 128 } // \t 188 if (sq_buf[12] & 0xff) != 0x4C { return 129 } // L 189 if (sq_buf[14] & 0xff) != 0x3A { return 130 } // : 190 if (sq_buf[15] & 0xff) != 0x32 { return 131 } // 2 (start of length) 191 if (sq_buf[23] & 0xff) != 0x32 { return 132 } // 2 (end of length) 192 if (sq_buf[24] & 0xff) != 0x0A { return 133 } // \n 193 194 // Smaller contig: "chrM" length=16569 -> "@SQ\tSN:chrM\tLN:16569\n" = 21 bytes 195 let chrM: *u8 = sys_mmap(8) 196 chrM[0]=0x63; chrM[1]=0x68; chrM[2]=0x72; chrM[3]=0x4D 197 let n_sq_m: i64 = sam_write_sq_line(chrM, 4, 16569, sq_buf, 64) 198 if n_sq_m != 21 { return n_sq_m + 500 } 199 200 // Bad inputs. 201 if sam_write_sq_line(chr1, 0, 100, sq_buf, 64) != -1 { return 140 } // empty chrom 202 if sam_write_sq_line(chr1, 4, 0, sq_buf, 64) != -1 { return 141 } // length=0 203 if sam_write_sq_line(chr1, 4, 100, sq_buf, 5) != -1 { return 142 } // capacity 204 205 // ============================================================ 206 // Section G -- sam_write_pg_line. 207 // id="nx_align" (8), name="nx_align" (8), version="0.1.0" (5). 208 // Expected: "@PG\tID:nx_align\tPN:nx_align\tVN:0.1.0\n" 209 // Length: 3 + 1 + 3 + 8 + 1 + 3 + 8 + 1 + 3 + 5 + 1 = 37 bytes 210 // ============================================================ 211 212 // str_buf = "nx_align" + "nx_align" + "0.1.0" = 8 + 8 + 5 = 21 bytes 213 let pg_strs: *u8 = sys_mmap(64) 214 pg_strs[0]=0x6E; pg_strs[1]=0x78; pg_strs[2]=0x5F // nx_ 215 pg_strs[3]=0x61; pg_strs[4]=0x6C; pg_strs[5]=0x69; pg_strs[6]=0x67; pg_strs[7]=0x6E // align 216 pg_strs[8]=0x6E; pg_strs[9]=0x78; pg_strs[10]=0x5F // nx_ 217 pg_strs[11]=0x61; pg_strs[12]=0x6C; pg_strs[13]=0x69; pg_strs[14]=0x67; pg_strs[15]=0x6E // align 218 pg_strs[16]=0x30; pg_strs[17]=0x2E; pg_strs[18]=0x31; pg_strs[19]=0x2E; pg_strs[20]=0x30 // 0.1.0 219 220 let pg_lens: *i64 = sys_mmap(64) as *i64 221 pg_lens[NX_SAM_PG_ID] = 8 222 pg_lens[NX_SAM_PG_NAME] = 8 223 pg_lens[NX_SAM_PG_VERSION] = 5 224 225 let pg_buf: *u8 = sys_mmap(128) 226 let n_pg: i64 = sam_write_pg_line(pg_strs, pg_lens, pg_buf, 128) 227 if n_pg != 37 { return n_pg + 600 } 228 229 // Spot-check key positions: 230 // 0:@ 1:P 2:G 3:\t 4:I 5:D 6:: 7:n 8:x 9:_ 10:a 11:l 12:i 13:g 14:n 231 // 15:\t 16:P 17:N 18:: 19-26:nx_align 27:\t 28:V 29:N 30:: 232 // 31:0 32:. 33:1 34:. 35:0 36:\n 233 if (pg_buf[0] & 0xff) != 0x40 { return 150 } // @ 234 if (pg_buf[1] & 0xff) != 0x50 { return 151 } // P 235 if (pg_buf[2] & 0xff) != 0x47 { return 152 } // G 236 if (pg_buf[3] & 0xff) != 0x09 { return 153 } // \t 237 if (pg_buf[4] & 0xff) != 0x49 { return 154 } // I 238 if (pg_buf[5] & 0xff) != 0x44 { return 155 } // D 239 if (pg_buf[6] & 0xff) != 0x3A { return 156 } // : 240 if (pg_buf[7] & 0xff) != 0x6E { return 157 } // n of nx_align 241 if (pg_buf[14] & 0xff) != 0x6E { return 158 } // n end of nx_align 242 if (pg_buf[15] & 0xff) != 0x09 { return 159 } // \t 243 if (pg_buf[16] & 0xff) != 0x50 { return 160 } // P 244 if (pg_buf[17] & 0xff) != 0x4E { return 161 } // N 245 if (pg_buf[18] & 0xff) != 0x3A { return 162 } // : 246 if (pg_buf[27] & 0xff) != 0x09 { return 163 } // \t before VN 247 if (pg_buf[28] & 0xff) != 0x56 { return 164 } // V 248 if (pg_buf[29] & 0xff) != 0x4E { return 165 } // N 249 if (pg_buf[31] & 0xff) != 0x30 { return 166 } // 0 of version 250 if (pg_buf[35] & 0xff) != 0x30 { return 167 } // 0 end of version 251 if (pg_buf[36] & 0xff) != 0x0A { return 168 } // \n 252 253 // Bad inputs. 254 pg_lens[NX_SAM_PG_ID] = 0 255 if sam_write_pg_line(pg_strs, pg_lens, pg_buf, 128) != -1 { return 170 } 256 pg_lens[NX_SAM_PG_ID] = 8 257 258 // ============================================================ 259 // Section H -- sam_write_rg_line. 260 // id="rg1" (3) + sample="patient_001" (11) -> str_buf 14 bytes. 261 // Expected: "@RG\tID:rg1\tSM:patient_001\n" 262 // Length: 3 + 1 + 3 + 3 + 1 + 3 + 11 + 1 = 26 bytes 263 // ============================================================ 264 265 let rg_strs: *u8 = sys_mmap(64) 266 rg_strs[0]=0x72; rg_strs[1]=0x67; rg_strs[2]=0x31 // rg1 267 rg_strs[3]=0x70; rg_strs[4]=0x61; rg_strs[5]=0x74; rg_strs[6]=0x69 // pati 268 rg_strs[7]=0x65; rg_strs[8]=0x6E; rg_strs[9]=0x74 // ent 269 rg_strs[10]=0x5F // _ 270 rg_strs[11]=0x30; rg_strs[12]=0x30; rg_strs[13]=0x31 // 001 271 272 let rg_lens: *i64 = sys_mmap(64) as *i64 273 rg_lens[NX_SAM_RG_ID] = 3 274 rg_lens[NX_SAM_RG_SAMPLE] = 11 275 276 let rg_buf: *u8 = sys_mmap(128) 277 let n_rg: i64 = sam_write_rg_line(rg_strs, rg_lens, rg_buf, 128) 278 if n_rg != 26 { return n_rg + 700 } 279 280 // Byte-by-byte: 281 // 0:@ 1:R 2:G 3:\t 4:I 5:D 6:: 7:r 8:g 9:1 10:\t 282 // 11:S 12:M 13:: 14:p 15:a 16:t 17:i 18:e 19:n 20:t 21:_ 283 // 22:0 23:0 24:1 25:\n 284 if (rg_buf[0] & 0xff) != 0x40 { return 180 } // @ 285 if (rg_buf[1] & 0xff) != 0x52 { return 181 } // R 286 if (rg_buf[2] & 0xff) != 0x47 { return 182 } // G 287 if (rg_buf[3] & 0xff) != 0x09 { return 183 } // \t 288 if (rg_buf[4] & 0xff) != 0x49 { return 184 } // I 289 if (rg_buf[5] & 0xff) != 0x44 { return 185 } // D 290 if (rg_buf[6] & 0xff) != 0x3A { return 186 } // : 291 if (rg_buf[7] & 0xff) != 0x72 { return 187 } // r 292 if (rg_buf[9] & 0xff) != 0x31 { return 188 } // 1 293 if (rg_buf[10] & 0xff) != 0x09 { return 189 } // \t 294 if (rg_buf[11] & 0xff) != 0x53 { return 190 } // S 295 if (rg_buf[12] & 0xff) != 0x4D { return 191 } // M 296 if (rg_buf[13] & 0xff) != 0x3A { return 192 } // : 297 if (rg_buf[14] & 0xff) != 0x70 { return 193 } // p (patient) 298 if (rg_buf[21] & 0xff) != 0x5F { return 194 } // _ 299 if (rg_buf[24] & 0xff) != 0x31 { return 195 } // 1 (end of 001) 300 if (rg_buf[25] & 0xff) != 0x0A { return 196 } // \n 301 302 // Bad input. 303 rg_lens[NX_SAM_RG_ID] = 0 304 if sam_write_rg_line(rg_strs, rg_lens, rg_buf, 128) != -1 { return 197 } 305 306 // ============================================================ 307 // Section I -- sam_append_tag_int (G6.0c). 308 // Append "\tNM:i:5" (7 bytes) to a buffer starting at off=0. 309 // ============================================================ 310 311 let tag_buf: *u8 = sys_mmap(128) 312 let off1: i64 = sam_append_tag_int(tag_buf, 0, 128, 0x4E, 0x4D, 5) 313 if off1 != 7 { return off1 + 800 } 314 // \t N M : i : 5 315 if (tag_buf[0] & 0xff) != 0x09 { return 200 } // \t 316 if (tag_buf[1] & 0xff) != 0x4E { return 201 } // N 317 if (tag_buf[2] & 0xff) != 0x4D { return 202 } // M 318 if (tag_buf[3] & 0xff) != 0x3A { return 203 } // : 319 if (tag_buf[4] & 0xff) != 0x69 { return 204 } // i 320 if (tag_buf[5] & 0xff) != 0x3A { return 205 } // : 321 if (tag_buf[6] & 0xff) != 0x35 { return 206 } // 5 322 323 // Chain: append AS:i:48 after. Expected: "\tNM:i:5\tAS:i:48" 324 let off2: i64 = sam_append_tag_int(tag_buf, off1, 128, 0x41, 0x53, 48) 325 if off2 != 15 { return off2 + 850 } // 7 (NM tag) + 8 (\tAS:i:48) 326 if (tag_buf[7] & 0xff) != 0x09 { return 210 } // \t 327 if (tag_buf[8] & 0xff) != 0x41 { return 211 } // A 328 if (tag_buf[9] & 0xff) != 0x53 { return 212 } // S 329 if (tag_buf[10] & 0xff) != 0x3A { return 213 } // : 330 if (tag_buf[11] & 0xff) != 0x69 { return 214 } // i 331 if (tag_buf[12] & 0xff) != 0x3A { return 215 } // : 332 if (tag_buf[13] & 0xff) != 0x34 { return 216 } // 4 333 if (tag_buf[14] & 0xff) != 0x38 { return 217 } // 8 334 335 // Negative value: NM:i:-3 -> "\tNM:i:-3" = 8 bytes 336 let tag_buf2: *u8 = sys_mmap(128) 337 let off_neg: i64 = sam_append_tag_int(tag_buf2, 0, 128, 0x4E, 0x4D, 0 - 3) 338 if off_neg != 8 { return off_neg + 900 } 339 if (tag_buf2[6] & 0xff) != 0x2D { return 220 } // '-' 340 if (tag_buf2[7] & 0xff) != 0x33 { return 221 } // '3' 341 342 // Multi-digit value: AS:i:12345 -> "\tAS:i:12345" = 11 bytes 343 let off_big: i64 = sam_append_tag_int(tag_buf, 0, 128, 0x41, 0x53, 12345) 344 if off_big != 11 { return off_big + 950 } 345 346 // Capacity overflow. 347 if sam_append_tag_int(tag_buf, 0, 5, 0x4E, 0x4D, 5) != -1 { return 230 } 348 349 // ============================================================ 350 // Section J -- sam_append_tag_str (G6.0c.2). 351 // Append "\tRG:Z:rg1" (9 bytes) starting at off=0. 352 // ============================================================ 353 354 let rg_id: *u8 = sys_mmap(8) 355 rg_id[0]=0x72; rg_id[1]=0x67; rg_id[2]=0x31 // "rg1" 356 357 let str_buf2: *u8 = sys_mmap(128) 358 let s1: i64 = sam_append_tag_str(str_buf2, 0, 128, 0x52, 0x47, rg_id, 3) 359 if s1 != 9 { return s1 + 950 } 360 // \t R G : Z : r g 1 361 if (str_buf2[0] & 0xff) != 0x09 { return 240 } 362 if (str_buf2[1] & 0xff) != 0x52 { return 241 } // R 363 if (str_buf2[2] & 0xff) != 0x47 { return 242 } // G 364 if (str_buf2[3] & 0xff) != 0x3A { return 243 } // : 365 if (str_buf2[4] & 0xff) != 0x5A { return 244 } // Z 366 if (str_buf2[5] & 0xff) != 0x3A { return 245 } // : 367 if (str_buf2[6] & 0xff) != 0x72 { return 246 } // r 368 if (str_buf2[7] & 0xff) != 0x67 { return 247 } // g 369 if (str_buf2[8] & 0xff) != 0x31 { return 248 } // 1 370 371 // Chain: append MD:Z:5T2A after. Total = 9 + (1 + 2 + 3 + 5) = 20 bytes. 372 let md_str: *u8 = sys_mmap(8) 373 md_str[0]=0x35; md_str[1]=0x54; md_str[2]=0x32; md_str[3]=0x41 // "5T2A" 374 // wait that's only 4 bytes. Recount: "5T2A" = 5(1) T(1) 2(1) A(1) = 4 bytes 375 let s2: i64 = sam_append_tag_str(str_buf2, s1, 128, 0x4D, 0x44, md_str, 4) 376 // Append adds 1(\t) + 2(MD) + 1(:) + 1(Z) + 1(:) + 4(5T2A) = 10 bytes 377 // Total = 9 + 10 = 19 378 if s2 != 19 { return s2 + 980 } 379 if (str_buf2[9] & 0xff) != 0x09 { return 260 } // \t 380 if (str_buf2[10] & 0xff) != 0x4D { return 261 } // M 381 if (str_buf2[11] & 0xff) != 0x44 { return 262 } // D 382 if (str_buf2[12] & 0xff) != 0x3A { return 263 } // : 383 if (str_buf2[13] & 0xff) != 0x5A { return 264 } // Z 384 if (str_buf2[14] & 0xff) != 0x3A { return 265 } // : 385 if (str_buf2[15] & 0xff) != 0x35 { return 266 } // 5 386 if (str_buf2[18] & 0xff) != 0x41 { return 267 } // A (end) 387 388 // Empty value not allowed (spec). 389 // Actually the spec is ambiguous; some tools accept it. G6.0c.2 390 // permits empty string (str_len=0); test that the 6-byte prefix 391 // emits cleanly. 392 let str_buf3: *u8 = sys_mmap(128) 393 let s_empty: i64 = sam_append_tag_str(str_buf3, 0, 128, 0x58, 0x58, rg_id, 0) 394 if s_empty != 6 { return s_empty + 990 } // "\tXX:Z:" = 6 bytes 395 396 // Capacity overflow. 397 if sam_append_tag_str(str_buf2, 0, 5, 0x52, 0x47, rg_id, 3) != -1 { return 270 } 398 399 // Negative str_len rejected. 400 if sam_append_tag_str(str_buf2, 0, 128, 0x52, 0x47, rg_id, -1) != -1 { return 271 } 401 402 // ============================================================ 403 // Section G -- paired-end SAM line writer (G6.0b). 404 // 405 // Canonical proper-pair FR read: 406 // QNAME=r1, FLAG=99, RNAME=chr1, POS=100, MAPQ=60, CIGAR=3M, 407 // RNEXT="=", PNEXT=250, TLEN=153, SEQ=ACG, QUAL=III 408 // 409 // Expected: "r1\t99\tchr1\t100\t60\t3M\t=\t250\t153\tACG\tIII\n" 410 // Length: r1(2)+\t+99(2)+\t+chr1(4)+\t+100(3)+\t+60(2)+\t+3M(2) 411 // +\t+=(1)+\t+250(3)+\t+153(3)+\t+ACG(3)+\t+III(3)+\n 412 // = 2+1+2+1+4+1+3+1+2+1+2+1+1+1+3+1+3+1+3+1+3+1 = 39 413 // ============================================================ 414 415 // str_buf_pe = "r1" + "chr1" + "=" + "3M" + "ACG" + "III" 416 // = 2 + 4 + 1 + 2 + 3 + 3 = 15 bytes 417 let str_buf_pe: *u8 = sys_mmap(64) 418 str_buf_pe[0]=0x72; str_buf_pe[1]=0x31 // r1 419 str_buf_pe[2]=0x63; str_buf_pe[3]=0x68; str_buf_pe[4]=0x72; str_buf_pe[5]=0x31 // chr1 420 str_buf_pe[6]=0x3D // = 421 str_buf_pe[7]=0x33; str_buf_pe[8]=0x4D // 3M 422 str_buf_pe[9]=0x41; str_buf_pe[10]=0x43; str_buf_pe[11]=0x47 // ACG 423 str_buf_pe[12]=0x49; str_buf_pe[13]=0x49; str_buf_pe[14]=0x49 // III 424 425 let str_lens_pe: *i64 = sys_mmap(8 * 5) as *i64 426 str_lens_pe[0] = 2 // qname 427 str_lens_pe[1] = 4 // rname 428 str_lens_pe[2] = 1 // rnext "=" 429 str_lens_pe[3] = 2 // cigar 430 str_lens_pe[4] = 3 // seq (qual implicit) 431 432 let fields_pe: *i64 = sys_mmap(8 * 5) as *i64 433 fields_pe[0] = 99 // flag 434 fields_pe[1] = 100 // pos 435 fields_pe[2] = 60 // mapq 436 fields_pe[3] = 250 // pnext 437 fields_pe[4] = 153 // tlen 438 439 let out_pe: *u8 = sys_mmap(128) 440 let pe_n: i64 = sam_write_line_paired(str_buf_pe, str_lens_pe, fields_pe, out_pe, 128) 441 if pe_n != 39 { return pe_n + 800 } 442 443 // Byte-by-byte: 444 // 0:r 1:1 2:\t 3:9 4:9 5:\t 6:c 7:h 8:r 9:1 10:\t 445 // 11:1 12:0 13:0 14:\t 15:6 16:0 17:\t 18:3 19:M 20:\t 446 // 21:= 22:\t 23:2 24:5 25:0 26:\t 27:1 28:5 29:3 30:\t 447 // 31:A 32:C 33:G 34:\t 35:I 36:I 37:I (then \n at 38) 448 if (out_pe[0] & 0xff) != 0x72 { return 300 } // r 449 if (out_pe[1] & 0xff) != 0x31 { return 301 } // 1 450 if (out_pe[2] & 0xff) != 0x09 { return 302 } // \t 451 if (out_pe[3] & 0xff) != 0x39 { return 303 } // 9 452 if (out_pe[4] & 0xff) != 0x39 { return 304 } // 9 453 if (out_pe[5] & 0xff) != 0x09 { return 305 } // \t 454 if (out_pe[6] & 0xff) != 0x63 { return 306 } // c 455 if (out_pe[10] & 0xff) != 0x09 { return 307 } // \t 456 if (out_pe[11] & 0xff) != 0x31 { return 308 } // 1 (pos) 457 if (out_pe[13] & 0xff) != 0x30 { return 309 } // 0 458 if (out_pe[14] & 0xff) != 0x09 { return 310 } // \t 459 if (out_pe[15] & 0xff) != 0x36 { return 311 } // 6 460 if (out_pe[16] & 0xff) != 0x30 { return 312 } // 0 461 if (out_pe[18] & 0xff) != 0x33 { return 313 } // 3 (cigar) 462 if (out_pe[19] & 0xff) != 0x4D { return 314 } // M 463 if (out_pe[20] & 0xff) != 0x09 { return 315 } // \t 464 if (out_pe[21] & 0xff) != 0x3D { return 316 } // = (rnext) 465 if (out_pe[22] & 0xff) != 0x09 { return 317 } // \t 466 if (out_pe[23] & 0xff) != 0x32 { return 318 } // 2 (pnext) 467 if (out_pe[24] & 0xff) != 0x35 { return 319 } // 5 468 if (out_pe[25] & 0xff) != 0x30 { return 320 } // 0 469 if (out_pe[26] & 0xff) != 0x09 { return 321 } // \t 470 if (out_pe[27] & 0xff) != 0x31 { return 322 } // 1 (tlen) 471 if (out_pe[28] & 0xff) != 0x35 { return 323 } // 5 472 if (out_pe[29] & 0xff) != 0x33 { return 324 } // 3 473 if (out_pe[30] & 0xff) != 0x09 { return 325 } // \t 474 if (out_pe[31] & 0xff) != 0x41 { return 326 } // A 475 if (out_pe[34] & 0xff) != 0x09 { return 327 } // \t 476 if (out_pe[37] & 0xff) != 0x49 { return 328 } // I 477 478 // Negative TLEN (rightmost mate of pair) -- replace pos/pnext for clarity. 479 fields_pe[1] = 253 // pos (rightmost mate) 480 fields_pe[3] = 100 // pnext (back to leftmost) 481 fields_pe[4] = -153 // tlen negative 482 let pe_n2: i64 = sam_write_line_paired(str_buf_pe, str_lens_pe, fields_pe, out_pe, 128) 483 // "r1\t99\tchr1\t253\t60\t3M\t=\t100\t-153\tACG\tIII\n" 484 // = 2+1+2+1+4+1+3+1+2+1+2+1+1+1+3+1+4+1+3+1+3+1 = 40 485 if pe_n2 != 40 { return pe_n2 + 850 } 486 if (out_pe[27] & 0xff) != 0x2D { return 340 } // '-' for negative tlen 487 if (out_pe[28] & 0xff) != 0x31 { return 341 } // 1 488 if (out_pe[29] & 0xff) != 0x35 { return 342 } // 5 489 if (out_pe[30] & 0xff) != 0x33 { return 343 } // 3 490 491 // TLEN=0 (mate unmapped / different chr). 492 fields_pe[4] = 0 493 let pe_n3: i64 = sam_write_line_paired(str_buf_pe, str_lens_pe, fields_pe, out_pe, 128) 494 // tlen "0" = 1 byte (instead of 3-char "153") → 39 - 2 = 37 495 if pe_n3 != 37 { return pe_n3 + 870 } 496 497 // Capacity overflow. 498 fields_pe[1] = 100 499 fields_pe[3] = 250 500 fields_pe[4] = 153 501 if sam_write_line_paired(str_buf_pe, str_lens_pe, fields_pe, out_pe, 10) != -1 { return 360 } 502 503 // Reject invalid: pnext < 0. 504 fields_pe[3] = -1 505 if sam_write_line_paired(str_buf_pe, str_lens_pe, fields_pe, out_pe, 128) != -1 { return 361 } 506 fields_pe[3] = 250 507 508 // Reject invalid: pos < 1. 509 fields_pe[1] = 0 510 if sam_write_line_paired(str_buf_pe, str_lens_pe, fields_pe, out_pe, 128) != -1 { return 362 } 511 fields_pe[1] = 100 512 513 // Reject invalid: empty rnext. 514 str_lens_pe[2] = 0 515 if sam_write_line_paired(str_buf_pe, str_lens_pe, fields_pe, out_pe, 128) != -1 { return 363 } 516 str_lens_pe[2] = 1 517 518 return 0 519}