Rename I/O available methods to length
diff --git a/doc/changelog.md b/doc/changelog.md
index 27150c5..b6053a6 100644
--- a/doc/changelog.md
+++ b/doc/changelog.md
@@ -37,6 +37,7 @@
 - Made `wuffs_base__status` a struct.
 - Removed `ack_metadata_chunk?`.
 - Removed `wuffs_base__frame_config__blend`.
+- Renamed I/O `available` methods to `length`.
 - Renamed `decode_io_writer?` methods to `transform_io?`.
 - Renamed `example/library` to `example/toy-genlib`.
 - Renamed `swizzle_interleaved!` to `swizzle_interleaved_from_slice!`.
diff --git a/internal/cgen/builtin.go b/internal/cgen/builtin.go
index 4d16b3e..596b4aa 100644
--- a/internal/cgen/builtin.go
+++ b/internal/cgen/builtin.go
@@ -150,7 +150,7 @@
 
 func (g *gen) writeBuiltinIO(b *buffer, recv *a.Expr, method t.ID, args []*a.Node, depth uint32) error {
 	switch method {
-	case t.IDAvailable:
+	case t.IDLength:
 		name, err := g.ioRecvName(recv)
 		if err != nil {
 			return err
diff --git a/lang/builtin/builtin.go b/lang/builtin/builtin.go
index a1999fd..c0e2d23 100644
--- a/lang/builtin/builtin.go
+++ b/lang/builtin/builtin.go
@@ -346,8 +346,8 @@
 	"io_reader.read_u64be?() u64",
 	"io_reader.read_u64le?() u64",
 
-	// TODO: these should have an explicit pre-condition "available() >= N".
-	// For now, that's implicitly checked (i.e. hard coded).
+	// TODO: these should have an explicit pre-condition "length() >= N". For
+	// now, that's implicitly checked (i.e. hard coded).
 	//
 	// io_reader has peek_etc methods and skip_u32_fast, not read_etc_fast,
 	// because we sometimes advance the pointer by less than what's read. See
@@ -387,9 +387,9 @@
 	// in practice.
 	"io_reader.peek_u64le_at(offset: u32[..= 0xFFF8]) u64",
 
-	"io_reader.available() u64",
 	"io_reader.count_since(mark: u64) u64",
 	"io_reader.is_closed() bool",
+	"io_reader.length() u64",
 	"io_reader.mark() u64",
 	"io_reader.match7(a: u64) u32[..= 2]",
 	"io_reader.position() u64",
@@ -402,7 +402,7 @@
 	"io_reader.skip_u32?(n: u32)",
 
 	// TODO: this should have explicit pre-conditions "actual <= worst_case"
-	// and "worst_case <= available()". For now, that's all implicitly checked
+	// and "worst_case <= length()". For now, that's all implicitly checked
 	// (i.e. hard coded).
 	"io_reader.skip_u32_fast!(actual: u32, worst_case: u32)",
 
@@ -424,8 +424,8 @@
 	"io_writer.write_u64be?(a: u64)",
 	"io_writer.write_u64le?(a: u64)",
 
-	// TODO: these should have an explicit pre-condition "available() >= N".
-	// For now, that's implicitly checked (i.e. hard coded).
+	// TODO: these should have an explicit pre-condition "length() >= N". For
+	// now, that's implicitly checked (i.e. hard coded).
 	//
 	// io_writer has write_etc_fast methods, not poke_etc and skip_u32_fast,
 	// because skip_u32_fast could leave uninitialized bytes in the io_buffer.
@@ -445,9 +445,9 @@
 	"io_writer.write_u64be_fast!(a: u64)",
 	"io_writer.write_u64le_fast!(a: u64)",
 
-	"io_writer.available() u64",
 	"io_writer.count_since(mark: u64) u64",
 	"io_writer.history_available() u64",
+	"io_writer.length() u64",
 	"io_writer.mark() u64",
 	"io_writer.position() u64",
 	"io_writer.since(mark: u64) slice u8",
@@ -458,7 +458,7 @@
 	"io_writer.limited_copy_u32_from_slice!(up_to: u32, s: slice u8) u32",
 
 	// TODO: this should have explicit pre-conditions:
-	//  - up_to <= this.available()
+	//  - up_to <= this.length()
 	//  - distance > 0
 	//  - distance <= this.since_mark().length()
 	// For now, that's all implicitly checked (i.e. hard coded).
@@ -473,7 +473,7 @@
 		"value_extension: u64[..= 0x3FFF_FFFF_FFFF]," +
 		"continued: u32[..= 0x1], length: u32[..= 0xFFFF])",
 
-	"token_writer.available() u64",
+	"token_writer.length() u64",
 
 	// ---- frame_config
 
diff --git a/lang/check/bounds.go b/lang/check/bounds.go
index b0ddc14..13eacb1 100644
--- a/lang/check/bounds.go
+++ b/lang/check/bounds.go
@@ -1085,7 +1085,7 @@
 				} else {
 					adv = advanceExpr.Str(q.tm)
 				}
-				return bounds{}, fmt.Errorf("check: could not prove %s pre-condition: %s.available() >= %s",
+				return bounds{}, fmt.Errorf("check: could not prove %s pre-condition: %s.length() >= %s",
 					method.Str(q.tm), recv.Str(q.tm), adv)
 			}
 			// TODO: drop other recv-related facts?
@@ -1120,7 +1120,7 @@
 
 func (q *checker) canLimitedCopyU32FromHistoryFast(recv *a.Expr, args []*a.Node) error {
 	// As per cgen's io-private.h, there are three pre-conditions:
-	//  - n <= this.available()
+	//  - n <= this.length()
 	//  - distance > 0
 	//  - distance <= this.history_available()
 
@@ -1130,7 +1130,7 @@
 	n := args[0].AsArg().Value()
 	distance := args[1].AsArg().Value()
 
-	// Check "n <= this.available()".
+	// Check "n <= this.length()".
 check0:
 	for {
 		for _, x := range q.facts {
@@ -1148,9 +1148,9 @@
 				continue
 			}
 
-			// Check that the RHS is "recv.available()".
+			// Check that the RHS is "recv.length()".
 			y, method, yArgs := splitReceiverMethodArgs(x.RHS().AsExpr())
-			if method != t.IDAvailable || len(yArgs) != 0 {
+			if method != t.IDLength || len(yArgs) != 0 {
 				continue
 			}
 			if !y.Eq(recv) {
@@ -1159,7 +1159,7 @@
 
 			break check0
 		}
-		return fmt.Errorf("check: could not prove n <= %s.available()", recv.Str(q.tm))
+		return fmt.Errorf("check: could not prove n <= %s.length()", recv.Str(q.tm))
 	}
 
 	// Check "distance > 0".
diff --git a/lang/check/optimize.go b/lang/check/optimize.go
index 4cdb968..c96dba0 100644
--- a/lang/check/optimize.go
+++ b/lang/check/optimize.go
@@ -55,7 +55,7 @@
 
 	retErr = q.facts.update(func(x *a.Expr) (*a.Expr, error) {
 		// TODO: update (discard?) any facts that merely mention
-		// receiver.available(), even if they aren't an exact match.
+		// receiver.length(), even if they aren't an exact match.
 
 		op := x.Operator()
 		if op != t.IDXBinaryGreaterEq && op != t.IDXBinaryGreaterThan {
@@ -67,13 +67,13 @@
 			return x, nil
 		}
 
-		// Check that lhs is "receiver.available()".
+		// Check that lhs is "receiver.length()".
 		lhs := x.LHS().AsExpr()
 		if lhs.Operator() != t.IDOpenParen || len(lhs.Args()) != 0 {
 			return x, nil
 		}
 		lhs = lhs.LHS().AsExpr()
-		if lhs.Operator() != t.IDDot || lhs.Ident() != t.IDAvailable {
+		if lhs.Operator() != t.IDDot || lhs.Ident() != t.IDLength {
 			return x, nil
 		}
 		lhs = lhs.LHS().AsExpr()
@@ -102,7 +102,7 @@
 
 		if rcv.Cmp(advance) == 0 {
 			// TODO: delete the (adjusted) fact, as newRCV will be zero, and
-			// "foo.available() >= 0" is redundant.
+			// "foo.length() >= 0" is redundant.
 		}
 
 		// Create a new a.Expr to hold the adjusted RHS constant value, newRCV.
@@ -121,20 +121,20 @@
 func (q *checker) optimizeIOMethodAdvanceExpr(receiver *a.Expr, advanceExpr *a.Expr, update bool) (retOK bool, retErr error) {
 	retErr = q.facts.update(func(x *a.Expr) (*a.Expr, error) {
 		// TODO: update (discard?) any facts that merely mention
-		// receiver.available(), even if they aren't an exact match.
+		// receiver.length(), even if they aren't an exact match.
 
 		op := x.Operator()
 		if op != t.IDXBinaryGreaterEq && op != t.IDXBinaryGreaterThan {
 			return x, nil
 		}
 
-		// Check that lhs is "receiver.available()".
+		// Check that lhs is "receiver.length()".
 		lhs := x.LHS().AsExpr()
 		if lhs.Operator() != t.IDOpenParen || len(lhs.Args()) != 0 {
 			return x, nil
 		}
 		lhs = lhs.LHS().AsExpr()
-		if lhs.Operator() != t.IDDot || lhs.Ident() != t.IDAvailable {
+		if lhs.Operator() != t.IDDot || lhs.Ident() != t.IDLength {
 			return x, nil
 		}
 		lhs = lhs.LHS().AsExpr()
diff --git a/lang/token/list.go b/lang/token/list.go
index 2a6debc..e6a754e 100644
--- a/lang/token/list.go
+++ b/lang/token/list.go
@@ -651,8 +651,6 @@
 	IDIsOK         = ID(0x231)
 	IDIsSuspension = ID(0x232)
 
-	IDAvailable = ID(0x250)
-
 	IDData            = ID(0x240)
 	IDHeight          = ID(0x241)
 	IDIO              = ID(0x242)
@@ -1016,8 +1014,6 @@
 	IDIsOK:         "is_ok",
 	IDIsSuspension: "is_suspension",
 
-	IDAvailable: "available",
-
 	IDData:            "data",
 	IDHeight:          "height",
 	IDIO:              "io",
diff --git a/std/bmp/decode_bmp.wuffs b/std/bmp/decode_bmp.wuffs
index 5a28511..589679e 100644
--- a/std/bmp/decode_bmp.wuffs
+++ b/std/bmp/decode_bmp.wuffs
@@ -297,7 +297,7 @@
 
 	while.outer true {
 		while this.pending_pad > 0 {
-			if args.src.available() <= 0 {
+			if args.src.length() <= 0 {
 				return "@internal note: short read"
 			}
 			this.pending_pad -= 1
diff --git a/std/cbor/decode_cbor.wuffs b/std/cbor/decode_cbor.wuffs
index 911f1fd..e9da238 100644
--- a/std/cbor/decode_cbor.wuffs
+++ b/std/cbor/decode_cbor.wuffs
@@ -148,11 +148,11 @@
 	while.outer true {
 		while.goto_parsed_a_leaf_value true {{
 		while.goto_fail true {{
-		if args.dst.available() <= 1 {
+		if args.dst.length() <= 1 {
 			yield? base."$short write"
 			continue.outer
 		}
-		if args.src.available() <= 0 {
+		if args.src.length() <= 0 {
 			if args.src.is_closed() {
 				return "#bad input"
 			}
@@ -189,28 +189,28 @@
 			string_length = c_minor as base.u64
 		} else {
 			while.goto_have_string_length true,
-				inv args.dst.available() > 1,
+				inv args.dst.length() > 1,
 			{{
 			if c_minor == 0x18 {
-				if args.src.available() >= 1 {
+				if args.src.length() >= 1 {
 					string_length = args.src.peek_u8_as_u64()
 					args.src.skip_u32_fast!(actual: 1, worst_case: 1)
 					break.goto_have_string_length
 				}
 			} else if c_minor == 0x19 {
-				if args.src.available() >= 2 {
+				if args.src.length() >= 2 {
 					string_length = args.src.peek_u16be_as_u64()
 					args.src.skip_u32_fast!(actual: 2, worst_case: 2)
 					break.goto_have_string_length
 				}
 			} else if c_minor == 0x1A {
-				if args.src.available() >= 4 {
+				if args.src.length() >= 4 {
 					string_length = args.src.peek_u32be_as_u64()
 					args.src.skip_u32_fast!(actual: 4, worst_case: 4)
 					break.goto_have_string_length
 				}
 			} else if c_minor == 0x1B {
-				if args.src.available() >= 8 {
+				if args.src.length() >= 8 {
 					string_length = args.src.peek_u64be()
 					args.src.skip_u32_fast!(actual: 8, worst_case: 8)
 					break.goto_have_string_length
@@ -326,11 +326,11 @@
 			}
 
 			while true {
-				if args.dst.available() <= 0 {
+				if args.dst.length() <= 0 {
 					yield? base."$short write"
 					continue
 				}
-				n64 = string_length.min(a: args.src.available())
+				n64 = string_length.min(a: args.src.length())
 				token_length = (n64 & 0xFFFF) as base.u32
 				if n64 > 0xFFFF {
 					token_length = 0xFFFF
@@ -341,7 +341,7 @@
 					yield? base."$short read"
 					continue
 				}
-				if args.src.available() < (token_length as base.u64) {
+				if args.src.length() < (token_length as base.u64) {
 					return "#internal error: inconsistent token length"
 				}
 				string_length ~mod-= token_length as base.u64
@@ -408,7 +408,7 @@
 			}
 
 			while true {
-				if args.dst.available() <= 0 {
+				if args.dst.length() <= 0 {
 					yield? base."$short write"
 					continue
 				}
@@ -417,13 +417,13 @@
 				token_length = (n64 & 0xFFFF) as base.u32
 				if token_length <= 0 {
 					// The longest UTF-8 code point is 4 bytes.
-					if args.src.is_closed() or (args.src.available() >= 4) {
+					if args.src.is_closed() or (args.src.length() >= 4) {
 						return "#bad input"
 					}
 					yield? base."$short read"
 					continue
 				}
-				if args.src.available() < (token_length as base.u64) {
+				if args.src.length() < (token_length as base.u64) {
 					return "#internal error: inconsistent token length"
 				}
 				string_length ~mod-= token_length as base.u64
@@ -736,9 +736,9 @@
 				continue.outer
 			}
 
-			while args.dst.available() <= 0,
+			while args.dst.length() <= 0,
 				inv depth > 0,
-				post args.dst.available() > 0,
+				post args.dst.length() > 0,
 			{
 				yield? base."$short write"
 				continue
diff --git a/std/deflate/decode_deflate.wuffs b/std/deflate/decode_deflate.wuffs
index 398fa06..df5c72f 100644
--- a/std/deflate/decode_deflate.wuffs
+++ b/std/deflate/decode_deflate.wuffs
@@ -299,7 +299,7 @@
 			return ok
 		}
 		length -= n_copied
-		if args.dst.available() == 0 {
+		if args.dst.length() == 0 {
 			yield? base."$short write"
 		} else {
 			yield? base."$short read"
diff --git a/std/deflate/decode_huffman_fast.wuffs b/std/deflate/decode_huffman_fast.wuffs
index b57cb9f..fce0b9c 100644
--- a/std/deflate/decode_huffman_fast.wuffs
+++ b/std/deflate/decode_huffman_fast.wuffs
@@ -57,12 +57,12 @@
 	// available input, because the H-L Literal/Length code is up to 15 bits
 	// plus up to 5 extra bits, the H-D Distance code is up to 15 bits plus up
 	// to 13 extra bits and 15 + 5 + 15 + 13 == 48. However,
-	// args.src.available() assertions are made in terms of bytes, not bits.
+	// args.src.length() assertions are made in terms of bytes, not bits.
 	//
 	// In theory, we could write assertions that mixed bytes and bits, such as:
 	//
-	// assert ((args.src.available() >= 6) and (n_bits >=  5)) or
-	//        ((args.src.available() >= 4) and (n_bits >= 21))
+	// assert ((args.src.length() >= 6) and (n_bits >=  5)) or
+	//        ((args.src.length() >= 4) and (n_bits >= 21))
 	//
 	// but that looks clumsy and makes the proofs more complicated. Instead, we
 	// are conservative and work in terms of bytes only. In bytes, each code
@@ -70,7 +70,7 @@
 	// for each. There are also up to 13 extra bits, or 2 bytes. Thus, each
 	// code requires up to 2 + 2 + 2 == 6 bytes, and there are two codes (one
 	// from H-L and one from H-D). Therefore, we check for at least 12 bytes.
-	while.loop(args.dst.available() >= 258) and (args.src.available() >= 12) {
+	while.loop(args.dst.length() >= 258) and (args.src.length() >= 12) {
 		// Ensure that we have at least 15 bits of input.
 		if n_bits < 15 {
 			assert n_bits >= 0  // TODO: this shouldn't be necessary.
@@ -82,12 +82,12 @@
 			n_bits += 8
 			assert n_bits >= 15
 		} else {
-			assert args.src.available() >= 10
+			assert args.src.length() >= 10
 		}
 		// These assertions are redundant, but are listed explicitly for
 		// clarity. They must hold regardless of which branch was taken for the
 		// if statement above.
-		assert args.src.available() >= 10
+		assert args.src.length() >= 10
 		assert n_bits >= 15
 
 		// Decode an lcode symbol from H-L.
@@ -102,7 +102,7 @@
 			continue.loop
 		} else if (table_entry >> 30) <> 0 {
 			// No-op; code continues past the if-else chain.
-			assert args.src.available() >= 8
+			assert args.src.length() >= 8
 		} else if (table_entry >> 29) <> 0 {
 			// End of block.
 			this.end_of_block = true
@@ -121,10 +121,10 @@
 				n_bits += 8
 				assert n_bits >= 15
 			} else {
-				assert args.src.available() >= 8
+				assert args.src.length() >= 8
 			}
 			// Once again, redundant but explicit assertions.
-			assert args.src.available() >= 8
+			assert args.src.length() >= 8
 			assert n_bits >= 15
 
 			redir_top = (table_entry >> 8) & 0xFFFF
@@ -153,7 +153,7 @@
 			}
 
 			// Once again, redundant but explicit assertions.
-			assert args.src.available() >= 8
+			assert args.src.length() >= 8
 
 		} else if (table_entry >> 27) <> 0 {
 			return "#bad Huffman code"
@@ -179,10 +179,10 @@
 				n_bits += 8
 				assert n_bits >= 15
 			} else {
-				assert args.src.available() >= 6
+				assert args.src.length() >= 6
 			}
 			// Once again, redundant but explicit assertions.
-			assert args.src.available() >= 6
+			assert args.src.length() >= 6
 			assert n_bits >= 15
 
 			// The "+ 253" is the same as "- 3", after the "& 0xFF", but the
@@ -192,7 +192,7 @@
 			n_bits -= table_entry_n_bits
 
 		} else {
-			assert args.src.available() >= 6
+			assert args.src.length() >= 6
 		}
 
 		// Ensure that we have at least 15 bits of input.
@@ -206,10 +206,10 @@
 			n_bits += 8
 			assert n_bits >= 15
 		} else {
-			assert args.src.available() >= 4
+			assert args.src.length() >= 4
 		}
 		// Once again, redundant but explicit assertions.
-		assert args.src.available() >= 4
+		assert args.src.length() >= 4
 		assert n_bits >= 15
 
 		// Decode a dcode symbol from H-D.
@@ -231,10 +231,10 @@
 				n_bits += 8
 				assert n_bits >= 15
 			} else {
-				assert args.src.available() >= 2
+				assert args.src.length() >= 2
 			}
 			// Once again, redundant but explicit assertions.
-			assert args.src.available() >= 2
+			assert args.src.length() >= 2
 			assert n_bits >= 15
 
 			redir_top = (table_entry >> 8) & 0xFFFF
@@ -244,7 +244,7 @@
 			bits >>= table_entry_n_bits
 			n_bits -= table_entry_n_bits
 		} else {
-			assert args.src.available() >= 2
+			assert args.src.length() >= 2
 		}
 
 		// For H-D, all symbols should be base_number + extra_bits.
@@ -287,7 +287,7 @@
 		// its presence minimizes the diff between decode_huffman_fast and
 		// decode_huffman_slow.
 		while true,
-			pre args.dst.available() >= 258,
+			pre args.dst.length() >= 258,
 		{
 			// Copy from this.history.
 			if ((dist_minus_1 + 1) as base.u64) > args.dst.history_available() {
@@ -316,7 +316,7 @@
 				//
 				// This copying is simpler than the decode_huffman_slow version
 				// because it cannot yield. We have already checked that
-				// args.dst.available() is large enough.
+				// args.dst.length() is large enough.
 				args.dst.limited_copy_u32_from_slice!(
 					up_to: hlen, s: this.history[hdist & 0x7FFF ..])
 
@@ -331,12 +331,12 @@
 			}
 			// Once again, redundant but explicit assertions.
 			assert ((dist_minus_1 + 1) as base.u64) <= args.dst.history_available()
-			assert args.dst.available() >= 258
+			assert args.dst.length() >= 258
 
 			// We can therefore prove:
 			assert (dist_minus_1 + 1) > 0
 			assert (length as base.u64) <= 258
-			assert (length as base.u64) <= args.dst.available() via "a <= b: a <= c; c <= b"(c: 258)
+			assert (length as base.u64) <= args.dst.length() via "a <= b: a <= c; c <= b"(c: 258)
 
 			// Copy from args.dst.
 			args.dst.limited_copy_u32_from_history_fast!(
diff --git a/std/gif/decode_config.wuffs b/std/gif/decode_config.wuffs
index f05e201..afe61fa 100644
--- a/std/gif/decode_config.wuffs
+++ b/std/gif/decode_config.wuffs
@@ -179,7 +179,7 @@
 
 	while true {
 		while true,
-			post args.src.available() > 0,
+			post args.src.length() > 0,
 		{
 			if args.src.position() <> this.metadata_io_position {
 				if args.minfo <> nullptr {
@@ -194,7 +194,7 @@
 				continue
 			}
 
-			if args.src.available() <= 0 {
+			if args.src.length() <= 0 {
 				if args.minfo <> nullptr {
 					args.minfo.set!(
 						flavor: 0,
@@ -322,8 +322,8 @@
 		if this.quirks[QUIRK_FIRST_FRAME_LOCAL_PALETTE_MEANS_BLACK_BACKGROUND - QUIRKS_BASE] and
 			(this.num_decoded_frame_configs_value == 0) {
 
-			while args.src.available() <= 0,
-				post args.src.available() > 0,
+			while args.src.length() <= 0,
+				post args.src.length() > 0,
 			{
 				yield? base."$short read"
 			} endwhile
diff --git a/std/gif/decode_gif.wuffs b/std/gif/decode_gif.wuffs
index 0dd4d40..fc024d6 100644
--- a/std/gif/decode_gif.wuffs
+++ b/std/gif/decode_gif.wuffs
@@ -218,7 +218,7 @@
 
 	while true {
 		while true,
-			post args.src.available() > 0,
+			post args.src.length() > 0,
 		{
 			if args.src.position() <> this.metadata_io_position {
 				if args.minfo <> nullptr {
@@ -233,7 +233,7 @@
 				continue
 			}
 
-			if args.src.available() <= 0 {
+			if args.src.length() <= 0 {
 				if args.minfo <> nullptr {
 					args.minfo.set!(
 						flavor: 0,
@@ -381,8 +381,8 @@
 		if this.quirks[QUIRK_FIRST_FRAME_LOCAL_PALETTE_MEANS_BLACK_BACKGROUND - QUIRKS_BASE] and
 			(this.num_decoded_frame_configs_value == 0) {
 
-			while args.src.available() <= 0,
-				post args.src.available() > 0,
+			while args.src.length() <= 0,
+				post args.src.length() > 0,
 			{
 				yield? base."$short read"
 			} endwhile
@@ -937,7 +937,7 @@
 		if block_size == 0 {
 			break.outer
 		}
-		while args.src.available() == 0 {
+		while args.src.length() == 0 {
 			yield? base."$short read"
 		} endwhile
 
@@ -946,7 +946,7 @@
 			this.compressed_wi = 0
 		}
 		while this.compressed_wi <= (4096 - 255) {
-			n_compressed = block_size.min(a: args.src.available())
+			n_compressed = block_size.min(a: args.src.length())
 			if n_compressed <= 0 {
 				break
 			}
@@ -958,7 +958,7 @@
 			if block_size > 0 {
 				break
 			}
-			if args.src.available() <= 0 {
+			if args.src.length() <= 0 {
 				need_block_size = true
 				break
 			}
diff --git a/std/json/decode_json.wuffs b/std/json/decode_json.wuffs
index a18c66a..cd601c5 100644
--- a/std/json/decode_json.wuffs
+++ b/std/json/decode_json.wuffs
@@ -138,7 +138,7 @@
 
 	while.outer true {
 		while.goto_parsed_a_leaf_value true {{
-		if args.dst.available() <= 0 {
+		if args.dst.length() <= 0 {
 			yield? base."$short write"
 			continue.outer
 		}
@@ -148,10 +148,10 @@
 		c = 0
 		class = 0
 		while.ws true,
-			inv args.dst.available() > 0,
-			post args.src.available() > 0,
+			inv args.dst.length() > 0,
+			post args.src.length() > 0,
 		{
-			if args.src.available() <= 0 {
+			if args.src.length() <= 0 {
 				if whitespace_length > 0 {
 					args.dst.write_simple_token_fast!(
 						value_major: 0,
@@ -195,7 +195,7 @@
 				continued: 0,
 				length: whitespace_length)
 			whitespace_length = 0
-			if args.dst.available() <= 0 {
+			if args.dst.length() <= 0 {
 				continue.outer
 			}
 		}
@@ -209,8 +209,8 @@
 		// know these facts; deleting these assertions should still compile)
 		// but are listed explicitly to guard against future edits to the code
 		// above inadvertently invalidating these assertions.
-		assert args.dst.available() > 0
-		assert args.src.available() > 0
+		assert args.dst.length() > 0
+		assert args.src.length() > 0
 
 		if class == CLASS_STRING {
 			// -------- BEGIN parse strings.
@@ -226,16 +226,16 @@
 			args.src.skip_u32_fast!(actual: 1, worst_case: 1)
 
 			while.string_loop_outer true {
-				if args.dst.available() <= 0 {
+				if args.dst.length() <= 0 {
 					yield? base."$short write"
 					continue.string_loop_outer
 				}
 
 				string_length = 0
 				while.string_loop_inner true,
-					pre args.dst.available() > 0,
+					pre args.dst.length() > 0,
 				{
-					if args.src.available() <= 0 {
+					if args.src.length() <= 0 {
 						if string_length > 0 {
 							args.dst.write_simple_token_fast!(
 								value_major: 0,
@@ -256,9 +256,9 @@
 
 					// As an optimization, consume non-special ASCII 4 bytes at
 					// a time.
-					while args.src.available() > 4,
-						inv args.dst.available() > 0,
-						inv args.src.available() > 0,
+					while args.src.length() > 4,
+						inv args.dst.length() > 0,
+						inv args.src.length() > 0,
 					{
 						c4 = args.src.peek_u32le()
 						if 0x00 <> (LUT_CHARS[0xFF & (c4 >> 0)] |
@@ -324,13 +324,13 @@
 								continued: 1,
 								length: string_length)
 							string_length = 0
-							if args.dst.available() <= 0 {
+							if args.dst.length() <= 0 {
 								continue.string_loop_outer
 							}
 						}
-						assert args.dst.available() > 0
+						assert args.dst.length() > 0
 
-						if args.src.available() < 2 {
+						if args.src.length() < 2 {
 							if args.src.is_closed() {
 								return "#bad backslash-escape"
 							}
@@ -365,7 +365,7 @@
 
 						} else if c == 'u' {
 							// -------- BEGIN backslash-u.
-							if args.src.available() < 6 {
+							if args.src.length() < 6 {
 								if args.src.is_closed() {
 									return "#bad backslash-escape"
 								}
@@ -417,7 +417,7 @@
 								// peeked 6 bytes for the high surrogate. We
 								// need 12 in total: another 8 bytes at an
 								// offset of 4.
-								if args.src.available() < 12 {
+								if args.src.length() < 12 {
 									if args.src.is_closed() {
 										if this.quirks[QUIRK_REPLACE_INVALID_UNICODE - QUIRKS_BASE] {
 											args.src.skip_u32_fast!(actual: 6, worst_case: 6)
@@ -484,7 +484,7 @@
 							}
 
 							if this.quirks[QUIRK_REPLACE_INVALID_UNICODE - QUIRKS_BASE] {
-								if args.src.available() < 6 {
+								if args.src.length() < 6 {
 									return "#internal error: inconsistent I/O"
 								}
 								args.src.skip_u32_fast!(actual: 6, worst_case: 6)
@@ -501,7 +501,7 @@
 						} else if (c == 'U') and
 							this.quirks[QUIRK_ALLOW_BACKSLASH_CAPITAL_U - QUIRKS_BASE] {
 							// -------- BEGIN backslash-capital-u.
-							if args.src.available() < 10 {
+							if args.src.length() < 10 {
 								if args.src.is_closed() {
 									return "#bad backslash-escape"
 								}
@@ -569,7 +569,7 @@
 						} else if (c == 'x') {
 							// -------- BEGIN backslash-x
 							if this.quirks[QUIRK_ALLOW_BACKSLASH_X_AS_CODE_POINTS - QUIRKS_BASE] {
-								if args.src.available() < 4 {
+								if args.src.length() < 4 {
 									if args.src.is_closed() {
 										return "#bad backslash-escape"
 									}
@@ -604,7 +604,7 @@
 									length: 4)
 								continue.string_loop_outer
 							} else if this.quirks[QUIRK_ALLOW_BACKSLASH_X_AS_BYTES - QUIRKS_BASE] {
-								if args.src.available() < 4 {
+								if args.src.length() < 4 {
 									if args.src.is_closed() {
 										return "#bad backslash-escape"
 									}
@@ -615,8 +615,8 @@
 								}
 
 								backslash_x_length = 0
-								while (backslash_x_length <= 0xFFFB) and (args.src.available() >= 4),
-									inv args.dst.available() > 0,
+								while (backslash_x_length <= 0xFFFB) and (args.src.length() >= 4),
+									inv args.dst.length() > 0,
 								{
 									backslash_x_string = args.src.peek_u32le()
 									backslash_x_ok = 0x80
@@ -654,7 +654,7 @@
 						return "#bad backslash-escape"
 
 					} else if char == 0x03 {  // 2-byte UTF-8.
-						if args.src.available() < 2 {
+						if args.src.length() < 2 {
 							if string_length > 0 {
 								args.dst.write_simple_token_fast!(
 									value_major: 0,
@@ -664,7 +664,7 @@
 									continued: 1,
 									length: string_length)
 								string_length = 0
-								if args.dst.available() <= 0 {
+								if args.dst.length() <= 0 {
 									continue.string_loop_outer
 								}
 							}
@@ -707,7 +707,7 @@
 						}
 
 					} else if char == 0x04 {  // 3-byte UTF-8.
-						if args.src.available() < 3 {
+						if args.src.length() < 3 {
 							if string_length > 0 {
 								args.dst.write_simple_token_fast!(
 									value_major: 0,
@@ -717,7 +717,7 @@
 									continued: 1,
 									length: string_length)
 								string_length = 0
-								if args.dst.available() <= 0 {
+								if args.dst.length() <= 0 {
 									continue.string_loop_outer
 								}
 							}
@@ -765,7 +765,7 @@
 						}
 
 					} else if char == 0x05 {  // 4-byte UTF-8.
-						if args.src.available() < 4 {
+						if args.src.length() < 4 {
 							if string_length > 0 {
 								args.dst.write_simple_token_fast!(
 									value_major: 0,
@@ -775,7 +775,7 @@
 									continued: 1,
 									length: string_length)
 								string_length = 0
-								if args.dst.available() <= 0 {
+								if args.dst.length() <= 0 {
 									continue.string_loop_outer
 								}
 							}
@@ -831,7 +831,7 @@
 							continued: 1,
 							length: string_length)
 						string_length = 0
-						if args.dst.available() <= 0 {
+						if args.dst.length() <= 0 {
 							continue.string_loop_outer
 						}
 					}
@@ -864,14 +864,14 @@
 
 			// Emit the trailing '"'.
 			while true {
-				if args.src.available() <= 0 {
+				if args.src.length() <= 0 {
 					if args.src.is_closed() {
 						return "#bad input"
 					}
 					yield? base."$short read"
 					continue
 				}
-				if args.dst.available() <= 0 {
+				if args.dst.length() <= 0 {
 					yield? base."$short write"
 					continue
 				}
@@ -936,7 +936,7 @@
 		} else if class == CLASS_NUMBER {
 			// -------- BEGIN parse numbers.
 			while true,
-				pre args.dst.available() > 0,
+				pre args.dst.length() > 0,
 			{
 				number_length = this.decode_number!(src: args.src)
 				number_status = number_length >> 8
@@ -978,8 +978,8 @@
 					return "#unsupported number length"
 				} else {
 					yield? base."$short read"
-					while args.dst.available() <= 0,
-						post args.dst.available() > 0,
+					while args.dst.length() <= 0,
+						post args.dst.length() > 0,
 					{
 						yield? base."$short write"
 					} endwhile
@@ -1151,7 +1151,7 @@
 					base.TOKEN__VBD__LITERAL__FALSE,
 					continued: 0,
 					length: 5)
-				if args.src.available() < 5 {
+				if args.src.length() < 5 {
 					return "#internal error: inconsistent I/O"
 				}
 				args.src.skip_u32_fast!(actual: 5, worst_case: 5)
@@ -1170,7 +1170,7 @@
 					base.TOKEN__VBD__LITERAL__TRUE,
 					continued: 0,
 					length: 4)
-				if args.src.available() < 4 {
+				if args.src.length() < 4 {
 					return "#internal error: inconsistent I/O"
 				}
 				args.src.skip_u32_fast!(actual: 4, worst_case: 4)
@@ -1189,7 +1189,7 @@
 					base.TOKEN__VBD__LITERAL__NULL,
 					continued: 0,
 					length: 4)
-				if args.src.available() < 4 {
+				if args.src.length() < 4 {
 					return "#internal error: inconsistent I/O"
 				}
 				args.src.skip_u32_fast!(actual: 4, worst_case: 4)
@@ -1239,7 +1239,7 @@
 	n = 0
 
 	// Peek.
-	if args.src.available() <= 0 {
+	if args.src.length() <= 0 {
 		if not args.src.is_closed() {
 			n |= 0x300
 		}
@@ -1249,14 +1249,14 @@
 
 	// Scan the optional minus sign.
 	if c <> '-' {
-		assert args.src.available() > 0
+		assert args.src.length() > 0
 		assert n <= 1
 	} else {
 		n += 1
 		args.src.skip_u32_fast!(actual: 1, worst_case: 1)
 
 		// Peek.
-		if args.src.available() <= 0 {
+		if args.src.length() <= 0 {
 			if not args.src.is_closed() {
 				n |= 0x300
 			}
@@ -1265,7 +1265,7 @@
 		}
 		c = args.src.peek_u8()
 
-		assert args.src.available() > 0
+		assert args.src.length() > 0
 		assert n <= 1
 	}
 
@@ -1283,7 +1283,7 @@
 	}
 
 	// Peek.
-	if args.src.available() <= 0 {
+	if args.src.length() <= 0 {
 		if not args.src.is_closed() {
 			n |= 0x300
 		}
@@ -1293,7 +1293,7 @@
 
 	// Scan the optional fraction.
 	if c <> '.' {
-		assert args.src.available() > 0
+		assert args.src.length() > 0
 		assert n <= 99
 	} else {
 		if n >= 99 {
@@ -1310,7 +1310,7 @@
 		}
 
 		// Peek.
-		if args.src.available() <= 0 {
+		if args.src.length() <= 0 {
 			if not args.src.is_closed() {
 				n |= 0x300
 			}
@@ -1318,7 +1318,7 @@
 		}
 		c = args.src.peek_u8()
 
-		assert args.src.available() > 0
+		assert args.src.length() > 0
 		assert n <= 99
 	}
 
@@ -1336,7 +1336,7 @@
 	assert n <= 99
 
 	// Peek.
-	if args.src.available() <= 0 {
+	if args.src.length() <= 0 {
 		if not args.src.is_closed() {
 			n |= 0x300
 		}
@@ -1373,7 +1373,7 @@
 
 	n = args.n
 	while true {
-		if args.src.available() <= 0 {
+		if args.src.length() <= 0 {
 			if not args.src.is_closed() {
 				n |= 0x300
 			}
@@ -1415,11 +1415,11 @@
 		this.quirks[QUIRK_ALLOW_LEADING_UNICODE_BYTE_ORDER_MARK - QUIRKS_BASE]
 
 	while this.allow_leading_ars or this.allow_leading_ubom {
-		if args.dst.available() <= 0 {
+		if args.dst.length() <= 0 {
 			yield? base."$short write"
 			continue
 		}
-		if args.src.available() <= 0 {
+		if args.src.length() <= 0 {
 			if args.src.is_closed() {
 				break
 			}
@@ -1434,7 +1434,7 @@
 				value_major: 0, value_minor: 0, continued: 0, length: 1)
 			continue
 		} else if (c == 0xEF) and this.allow_leading_ubom {
-			if args.src.available() < 3 {
+			if args.src.length() < 3 {
 				if args.src.is_closed() {
 					break
 				}
@@ -1459,11 +1459,11 @@
 	var c2     : base.u16
 	var length : base.u32[..= 0xFFFD]
 
-	while (args.dst.available() <= 0) or (args.src.available() <= 1),
-		post args.dst.available() > 0,
-		post args.src.available() > 1,
+	while (args.dst.length() <= 0) or (args.src.length() <= 1),
+		post args.dst.length() > 0,
+		post args.src.length() > 1,
 	{
-		if args.dst.available() <= 0 {
+		if args.dst.length() <= 0 {
 			yield? base."$short write"
 			continue
 		}
@@ -1479,16 +1479,16 @@
 		length = 2
 
 		while.comment_block true {
-			if args.dst.available() <= 0 {
+			if args.dst.length() <= 0 {
 				yield? base."$short write"
 				length = 0
 				continue.comment_block
 			}
 
 			while true,
-				pre args.dst.available() > 0,
+				pre args.dst.length() > 0,
 			{
-				if args.src.available() <= 1 {
+				if args.src.length() <= 1 {
 					if length > 0 {
 						args.dst.write_simple_token_fast!(
 							value_major: 0,
@@ -1537,16 +1537,16 @@
 		length = 2
 
 		while.comment_line true {
-			if args.dst.available() <= 0 {
+			if args.dst.length() <= 0 {
 				yield? base."$short write"
 				length = 0
 				continue.comment_line
 			}
 
 			while true,
-				pre args.dst.available() > 0,
+				pre args.dst.length() > 0,
 			{
-				if args.src.available() <= 0 {
+				if args.src.length() <= 0 {
 					if length > 0 {
 						args.dst.write_simple_token_fast!(
 							value_major: 0,
@@ -1599,11 +1599,11 @@
 	var neg : base.u32[..= 1]
 
 	while true {
-		if args.dst.available() <= 0 {
+		if args.dst.length() <= 0 {
 			yield? base."$short write"
 			continue
 		}
-		if args.src.available() <= 2 {
+		if args.src.length() <= 2 {
 			if args.src.is_closed() {
 				return "#bad input"
 			}
@@ -1615,7 +1615,7 @@
 
 		c4 = args.src.peek_u24le_as_u32()
 		if (c4 | 0x20_2020) == 'inf'le {
-			if args.src.available() > 7 {
+			if args.src.length() > 7 {
 				if (args.src.peek_u64le() | 0x2020_2020_2020_2020) == 'infinity'le {
 					args.dst.write_simple_token_fast!(
 						value_major: 0,
@@ -1656,7 +1656,7 @@
 			return "#bad input"
 		}
 
-		if args.src.available() <= 3 {
+		if args.src.length() <= 3 {
 			if args.src.is_closed() {
 				return "#bad input"
 			}
@@ -1666,7 +1666,7 @@
 
 		c4 = args.src.peek_u32le() >> 8
 		if (c4 | 0x20_2020) == 'inf'le {
-			if args.src.available() > 8 {
+			if args.src.length() > 8 {
 				if (args.src.peek_u64le_at(offset: 1) | 0x2020_2020_2020_2020) == 'infinity'le {
 					args.dst.write_simple_token_fast!(
 						value_major: 0,
@@ -1710,16 +1710,16 @@
 	var whitespace_length : base.u32[..= 0xFFFE]
 
 	while.outer true {
-		if args.dst.available() <= 0 {
+		if args.dst.length() <= 0 {
 			yield? base."$short write"
 			whitespace_length = 0
 			continue.outer
 		}
 
 		while.inner true,
-			pre args.dst.available() > 0,
+			pre args.dst.length() > 0,
 		{
-			if args.src.available() <= 0 {
+			if args.src.length() <= 0 {
 				if whitespace_length > 0 {
 					args.dst.write_simple_token_fast!(
 						value_major: 0, value_minor: 0, continued: 0, length: whitespace_length)
diff --git a/std/lzw/decode_lzw.wuffs b/std/lzw/decode_lzw.wuffs
index 59a34c1..b653eb7 100644
--- a/std/lzw/decode_lzw.wuffs
+++ b/std/lzw/decode_lzw.wuffs
@@ -158,7 +158,7 @@
 	while true {
 		if n_bits < width {
 			assert n_bits < 12 via "a < b: a < c; c <= b"(c: width)
-			if args.src.available() >= 4 {
+			if args.src.length() >= 4 {
 				// Read 4 bytes, using the "Variant 4" technique of
 				// https://fgiesen.wordpress.com/2018/02/20/reading-bits-in-far-too-many-ways-part-2/
 				bits |= args.src.peek_u32le() ~mod<< n_bits
@@ -166,7 +166,7 @@
 				n_bits |= 24
 				assert width <= n_bits via "a <= b: a <= c; c <= b"(c: 12)
 				assert n_bits >= width via "a >= b: b <= a"()
-			} else if args.src.available() <= 0 {
+			} else if args.src.length() <= 0 {
 				this.read_from_return_value = 2
 				break
 			} else {
@@ -175,7 +175,7 @@
 				n_bits += 8
 				if n_bits >= width {
 					// No-op.
-				} else if args.src.available() <= 0 {
+				} else if args.src.length() <= 0 {
 					this.read_from_return_value = 2
 					break
 				} else {
diff --git a/std/wbmp/decode_wbmp.wuffs b/std/wbmp/decode_wbmp.wuffs
index 070bb57..931461d 100644
--- a/std/wbmp/decode_wbmp.wuffs
+++ b/std/wbmp/decode_wbmp.wuffs
@@ -180,10 +180,10 @@
 				assert dst_x < 0xFFFF_FFFF via "a < b: a < c; c <= b"(c: this.width)
 
 				if (dst_x & 7) == 0 {
-					while args.src.available() <= 0,
+					while args.src.length() <= 0,
 						inv dst_x < 0xFFFF_FFFF,
 						inv dst_y < 0xFFFF_FFFF,
-						post args.src.available() > 0,
+						post args.src.length() > 0,
 					{
 						yield? base."$short read"
 						tab = args.dst.plane(p: 0)