// File: soybin[P]idyll/src/lib/TinyPngOut.cpp
// Function: TinyPngOut::write(unsigned char const*, unsigned long)
void TinyPngOut::write(const uint8_t pixels[], size_t count) {
	if (count > SIZE_MAX / 3)
		throw std::length_error("Invalid argument");
	count *= 3;  // Convert pixel count to byte count
	while (count > 0) {
		if (pixels == nullptr)
			throw std::invalid_argument("Null pointer");
		if (positionY >= height)
			throw std::logic_error("All image pixels already written");
		
		if (deflateFilled == 0) {  // Start DEFLATE block
			uint16_t size = DEFLATE_MAX_BLOCK_SIZE;
			if (uncompRemain < size)
				size = static_cast<uint16_t>(uncompRemain);
			const uint8_t header[] = {  // 5 bytes long
				static_cast<uint8_t>(uncompRemain <= DEFLATE_MAX_BLOCK_SIZE ? 1 : 0),
				static_cast<uint8_t>(size >> 0),
				static_cast<uint8_t>(size >> 8),
				static_cast<uint8_t>((size >> 0) ^ 0xFF),
				static_cast<uint8_t>((size >> 8) ^ 0xFF),
			};
			write(header);
			crc32(header, sizeof(header) / sizeof(header[0]));
		}
		assert(positionX < lineSize && deflateFilled < DEFLATE_MAX_BLOCK_SIZE);
		
		if (positionX == 0) {  // Beginning of line - write filter method byte
			uint8_t b[] = {0};
			write(b);
			crc32(b, 1);
			adler32(b, 1);
			positionX++;
			uncompRemain--;
			deflateFilled++;
			
		} else {  // Write some pixel bytes for current line
			uint16_t n = DEFLATE_MAX_BLOCK_SIZE - deflateFilled;
			if (lineSize - positionX < n)
				n = static_cast<uint16_t>(lineSize - positionX);
			if (count < n)
				n = static_cast<uint16_t>(count);
			if (static_cast<std::make_unsigned<std::streamsize>::type>(std::numeric_limits<std::streamsize>::max()) < std::numeric_limits<decltype(n)>::max())
				n = std::min(n, static_cast<decltype(n)>(std::numeric_limits<std::streamsize>::max()));
			assert(n > 0);
			output.write(reinterpret_cast<const char*>(pixels), static_cast<std::streamsize>(n));
			
			// Update checksums
			crc32(pixels, n);
			adler32(pixels, n);
			
			// Increment positions
			count -= n;
			pixels += n;
			positionX += n;
			uncompRemain -= n;
			deflateFilled += n;
		}
		
		if (deflateFilled >= DEFLATE_MAX_BLOCK_SIZE)
			deflateFilled = 0;  // End current block
		
		if (positionX == lineSize) {  // Increment line
			positionX = 0;
			positionY++;
			if (positionY == height) {  // Reached end of pixels
				uint8_t footer[] = {  // 20 bytes long
					0, 0, 0, 0,  // DEFLATE Adler-32 placeholder
					0, 0, 0, 0,  // IDAT CRC-32 placeholder
					// IEND chunk
					0x00, 0x00, 0x00, 0x00,
					0x49, 0x45, 0x4E, 0x44,
					0xAE, 0x42, 0x60, 0x82,
				};
				putBigUint32(adler, &footer[0]);
				crc32(&footer[0], 4);
				putBigUint32(crc, &footer[4]);
				write(footer);
			}
		}
	}
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x80, %rsp
movq %rdi, -0x8(%rbp)
movq %rsi, -0x10(%rbp)
movq %rdx, -0x18(%rbp)
movq -0x8(%rbp), %rax
movq %rax, -0x58(%rbp)
movabsq $0x5555555555555555, %rax # imm = 0x5555555555555555
cmpq %rax, -0x18(%rbp)
jbe 0x1bbb2
movl $0x10, %edi
callq 0x4160
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0x60(%rbp)
leaq 0x205b(%rip), %rsi      # 0x1dbd5
callq 0x4410
jmp 0x1bb81
movq -0x60(%rbp), %rdi
movq 0xb43c(%rip), %rsi      # 0x26fc8
movq 0xb40d(%rip), %rdx      # 0x26fa0
callq 0x4590
movq -0x60(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
callq 0x4280
jmp 0x1bf84
imulq $0x3, -0x18(%rbp), %rax
movq %rax, -0x18(%rbp)
cmpq $0x0, -0x18(%rbp)
jbe 0x1bf7b
cmpq $0x0, -0x10(%rbp)
jne 0x1bc20
movl $0x10, %edi
callq 0x4160
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0x68(%rbp)
leaq 0x1ffe(%rip), %rsi      # 0x1dbe6
callq 0x4630
jmp 0x1bbef
movq -0x68(%rbp), %rdi
movq 0xb3e6(%rip), %rsi      # 0x26fe0
movq 0xb3bf(%rip), %rdx      # 0x26fc0
callq 0x4590
movq -0x68(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
callq 0x4280
jmp 0x1bf84
movq -0x58(%rbp), %rcx
movl 0x1c(%rcx), %eax
cmpl 0x4(%rcx), %eax
jb 0x1bc7f
movl $0x10, %edi
callq 0x4160
movq %rax, %rdi
movq %rdi, %rax
movq %rax, -0x70(%rbp)
leaq 0x1fac(%rip), %rsi      # 0x1dbf3
callq 0x44c0
jmp 0x1bc4e
movq -0x70(%rbp), %rdi
movq 0xb37f(%rip), %rsi      # 0x26fd8
movq 0xb348(%rip), %rdx      # 0x26fa8
callq 0x4590
movq -0x70(%rbp), %rdi
movq %rax, %rcx
movl %edx, %eax
movq %rcx, -0x20(%rbp)
movl %eax, -0x24(%rbp)
callq 0x4280
jmp 0x1bf84
movq -0x58(%rbp), %rax
movzwl 0x24(%rax), %eax
cmpl $0x0, %eax
jne 0x1bd17
movq -0x58(%rbp), %rax
movw $0xffff, -0x26(%rbp)    # imm = 0xFFFF
movl 0x20(%rax), %eax
movzwl -0x26(%rbp), %ecx
cmpl %ecx, %eax
jae 0x1bcb0
movq -0x58(%rbp), %rax
movl 0x20(%rax), %eax
movw %ax, -0x26(%rbp)
movq -0x58(%rbp), %rdi
movl 0x20(%rdi), %edx
xorl %eax, %eax
movl $0x1, %ecx
cmpl $0xffff, %edx           # imm = 0xFFFF
cmovbel %ecx, %eax
movb %al, -0x2b(%rbp)
movzwl -0x26(%rbp), %eax
sarl $0x0, %eax
movb %al, -0x2a(%rbp)
movzwl -0x26(%rbp), %eax
sarl $0x8, %eax
movb %al, -0x29(%rbp)
movzwl -0x26(%rbp), %eax
sarl $0x0, %eax
xorl $0xff, %eax
movb %al, -0x28(%rbp)
movzwl -0x26(%rbp), %eax
sarl $0x8, %eax
xorl $0xff, %eax
movb %al, -0x27(%rbp)
leaq -0x2b(%rbp), %rsi
callq 0x1c050
movq -0x58(%rbp), %rdi
leaq -0x2b(%rbp), %rsi
movl $0x5, %edx
callq 0x1ba80
movq -0x58(%rbp), %rdx
movl 0x18(%rdx), %ecx
xorl %eax, %eax
cmpl 0x8(%rdx), %ecx
movb %al, -0x71(%rbp)
jae 0x1bd3b
movq -0x58(%rbp), %rax
movzwl 0x24(%rax), %eax
cmpl $0xffff, %eax           # imm = 0xFFFF
setl %al
movb %al, -0x71(%rbp)
movb -0x71(%rbp), %al
testb $0x1, %al
jne 0x1bd44
jmp 0x1bd46
jmp 0x1bd65
leaq 0x1ec7(%rip), %rdi      # 0x1dc14
leaq 0x1eff(%rip), %rsi      # 0x1dc53
movl $0x7a, %edx
leaq 0x1f0a(%rip), %rcx      # 0x1dc6a
callq 0x4220
movq -0x58(%rbp), %rax
cmpl $0x0, 0x18(%rax)
jne 0x1bdd7
leaq -0x2c(%rbp), %rdi
xorl %esi, %esi
movl $0x1, %edx
callq 0x4190
movq -0x58(%rbp), %rdi
leaq -0x2c(%rbp), %rsi
callq 0x1c080
movq -0x58(%rbp), %rdi
leaq -0x2c(%rbp), %rsi
movl $0x1, %edx
callq 0x1ba80
movq -0x58(%rbp), %rdi
leaq -0x2c(%rbp), %rsi
movl $0x1, %edx
callq 0x1bf90
movq -0x58(%rbp), %rax
movl 0x18(%rax), %ecx
addl $0x1, %ecx
movl %ecx, 0x18(%rax)
movl 0x20(%rax), %ecx
addl $-0x1, %ecx
movl %ecx, 0x20(%rax)
movw 0x24(%rax), %cx
addw $0x1, %cx
movw %cx, 0x24(%rax)
jmp 0x1becd
movq -0x58(%rbp), %rcx
movzwl 0x24(%rcx), %edx
movl $0xffff, %eax           # imm = 0xFFFF
subl %edx, %eax
movw %ax, -0x2e(%rbp)
movl 0x8(%rcx), %eax
subl 0x18(%rcx), %eax
movzwl -0x2e(%rbp), %ecx
cmpl %ecx, %eax
jae 0x1be06
movq -0x58(%rbp), %rcx
movl 0x8(%rcx), %eax
subl 0x18(%rcx), %eax
movw %ax, -0x2e(%rbp)
movq -0x18(%rbp), %rax
movzwl -0x2e(%rbp), %ecx
cmpq %rcx, %rax
jae 0x1be1b
movq -0x18(%rbp), %rax
movw %ax, -0x2e(%rbp)
movzwl -0x2e(%rbp), %eax
cmpl $0x0, %eax
jle 0x1be26
jmp 0x1be45
leaq 0x1e6d(%rip), %rdi      # 0x1dc9a
leaq 0x1e1f(%rip), %rsi      # 0x1dc53
movl $0x8d, %edx
leaq 0x1e2a(%rip), %rcx      # 0x1dc6a
callq 0x4220
movq -0x58(%rbp), %rax
movq 0x10(%rax), %rdi
movq -0x10(%rbp), %rsi
movzwl -0x2e(%rbp), %eax
movl %eax, %edx
callq 0x4470
movq -0x58(%rbp), %rdi
movq -0x10(%rbp), %rsi
movzwl -0x2e(%rbp), %eax
movl %eax, %edx
callq 0x1ba80
movq -0x58(%rbp), %rdi
movq -0x10(%rbp), %rsi
movzwl -0x2e(%rbp), %eax
movl %eax, %edx
callq 0x1bf90
movq -0x58(%rbp), %rax
movzwl -0x2e(%rbp), %ecx
movl %ecx, %edx
movq -0x18(%rbp), %rcx
subq %rdx, %rcx
movq %rcx, -0x18(%rbp)
movzwl -0x2e(%rbp), %edx
movq -0x10(%rbp), %rcx
movslq %edx, %rdx
addq %rdx, %rcx
movq %rcx, -0x10(%rbp)
movzwl -0x2e(%rbp), %ecx
addl 0x18(%rax), %ecx
movl %ecx, 0x18(%rax)
movzwl -0x2e(%rbp), %edx
movl 0x20(%rax), %ecx
subl %edx, %ecx
movl %ecx, 0x20(%rax)
movzwl -0x2e(%rbp), %edx
movzwl 0x24(%rax), %ecx
addl %edx, %ecx
movw %cx, 0x24(%rax)
movq -0x58(%rbp), %rax
movzwl 0x24(%rax), %eax
cmpl $0xffff, %eax           # imm = 0xFFFF
jl 0x1bee6
movq -0x58(%rbp), %rax
movw $0x0, 0x24(%rax)
movq -0x58(%rbp), %rcx
movl 0x18(%rcx), %eax
cmpl 0x8(%rcx), %eax
jne 0x1bf76
movq -0x58(%rbp), %rcx
movl $0x0, 0x18(%rcx)
movl 0x1c(%rcx), %eax
addl $0x1, %eax
movl %eax, 0x1c(%rcx)
movl 0x1c(%rcx), %eax
cmpl 0x4(%rcx), %eax
jne 0x1bf74
movq -0x58(%rbp), %rax
movq 0x1db3(%rip), %rcx      # 0x1dcd0
movq %rcx, -0x50(%rbp)
movq 0x1db0(%rip), %rcx      # 0x1dcd8
movq %rcx, -0x48(%rbp)
movl 0x1dae(%rip), %ecx      # 0x1dce0
movl %ecx, -0x40(%rbp)
movl 0x2c(%rax), %edi
leaq -0x50(%rbp), %rsi
callq 0x1ba30
movq -0x58(%rbp), %rdi
leaq -0x50(%rbp), %rsi
movl $0x4, %edx
callq 0x1ba80
movq -0x58(%rbp), %rax
movl 0x28(%rax), %edi
leaq -0x50(%rbp), %rsi
addq $0x4, %rsi
callq 0x1ba30
movq -0x58(%rbp), %rdi
leaq -0x50(%rbp), %rsi
callq 0x1c0b0
jmp 0x1bf76
jmp 0x1bbbb
addq $0x80, %rsp
popq %rbp
retq
movq -0x20(%rbp), %rdi
callq 0x45d0
nopl (%rax)