patch 8.1.0735: cannot handle binary data

Problem:    Cannot handle binary data.
Solution:   Add the Blob type. (Yasuhiro Matsumoto, closes #3638)
diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak
index 8d83516..6ea0c45 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -73,6 +73,7 @@
 	test_backspace_opt \
 	test_backup \
 	test_behave \
+	test_blob \
 	test_blockedit \
 	test_breakindent \
 	test_bufline \
@@ -283,6 +284,7 @@
 	test_autocmd.res \
 	test_autoload.res \
 	test_backspace_opt.res \
+	test_blob.res \
 	test_blockedit.res \
 	test_breakindent.res \
 	test_bufwintabinfo.res \
diff --git a/src/testdir/test_blob.vim b/src/testdir/test_blob.vim
new file mode 100644
index 0000000..4ab28eb4
--- /dev/null
+++ b/src/testdir/test_blob.vim
@@ -0,0 +1,179 @@
+" Tests for the Blob types
+
+func TearDown()
+  " Run garbage collection after every test
+  call test_garbagecollect_now()
+endfunc
+
+" Tests for Blob type
+
+" Blob creation from constant
+func Test_blob_create()
+  let b = 0zDEADBEEF
+  call assert_equal(v:t_blob, type(b))
+  call assert_equal(4, len(b))
+  call assert_equal(0xDE, b[0])
+  call assert_equal(0xAD, b[1])
+  call assert_equal(0xBE, b[2])
+  call assert_equal(0xEF, b[3])
+  call assert_fails('let x = b[4]')
+
+  call assert_equal(0xDE, get(b, 0))
+  call assert_equal(0xEF, get(b, 3))
+  call assert_fails('let x = get(b, 4)')
+endfunc
+
+" assignment to a blob
+func Test_blob_assign()
+  let b = 0zDEADBEEF
+  let b2 = b[1:2]
+  call assert_equal(0zADBE, b2)
+
+  let bcopy = b[:]
+  call assert_equal(b, bcopy)
+  call assert_false(b is bcopy)
+endfunc
+
+func Test_blob_to_string()
+  let b = 0zDEADBEEF
+  call assert_equal('[0xDE,0xAD,0xBE,0xEF]', string(b))
+  call remove(b, 0, 3)
+  call assert_equal('[]', string(b))
+endfunc
+
+func Test_blob_compare()
+  let b1 = 0z0011
+  let b2 = 0z1100
+  call assert_false(b1 == b2)
+  call assert_true(b1 != b2)
+  call assert_true(b1 == 0z0011)
+
+  call assert_false(b1 is b2)
+  let b2 = b1
+  call assert_true(b1 is b2)
+
+  call assert_fails('let x = b1 > b2')
+  call assert_fails('let x = b1 < b2')
+  call assert_fails('let x = b1 - b2')
+  call assert_fails('let x = b1 / b2')
+  call assert_fails('let x = b1 * b2')
+endfunc
+
+" test for range assign
+func Test_blob_range_assign()
+  let b = 0z00
+  let b[1] = 0x11
+  let b[2] = 0x22
+  call assert_equal(0z001122, b)
+  call assert_fails('let b[4] = 0x33')
+endfunc
+
+func Test_blob_for_loop()
+  let blob = 0z00010203
+  let i = 0
+  for byte in blob
+    call assert_equal(i, byte)
+    let i += 1
+  endfor
+
+  let blob = 0z00
+  call remove(blob, 0)
+  call assert_equal(0, len(blob))
+  for byte in blob
+    call assert_error('loop over empty blob')
+  endfor
+endfunc
+
+func Test_blob_concatenate()
+  let b = 0z0011
+  let b += 0z2233
+  call assert_equal(0z00112233, b)
+
+  call assert_fails('let b += "a"')
+  call assert_fails('let b += 88')
+
+  let b = 0zDEAD + 0zBEEF
+  call assert_equal(0zDEADBEEF, b)
+endfunc
+
+" Test removing items in blob
+func Test_blob_func_remove()
+  " Test removing 1 element
+  let b = 0zDEADBEEF
+  call assert_equal(0xDE, remove(b, 0))
+  call assert_equal(0zADBEEF, b)
+
+  let b = 0zDEADBEEF
+  call assert_equal(0xEF, remove(b, -1))
+  call assert_equal(0zDEADBE, b)
+
+  let b = 0zDEADBEEF
+  call assert_equal(0xAD, remove(b, 1))
+  call assert_equal(0zDEBEEF, b)
+
+  " Test removing range of element(s)
+  let b = 0zDEADBEEF
+  call assert_equal(0zBE, remove(b, 2, 2))
+  call assert_equal(0zDEADEF, b)
+
+  let b = 0zDEADBEEF
+  call assert_equal(0zADBE, remove(b, 1, 2))
+  call assert_equal(0zDEEF, b)
+
+  " Test invalid cases
+  let b = 0zDEADBEEF
+  call assert_fails("call remove(b, 5)", 'E979:')
+  call assert_fails("call remove(b, 1, 5)", 'E979:')
+  call assert_fails("call remove(b, 3, 2)", 'E979:')
+  call assert_fails("call remove(1, 0)", 'E712:')
+  call assert_fails("call remove(b, b)", 'E974:')
+endfunc
+
+func Test_blob_read_write()
+  let b = 0zDEADBEEF
+  call writefile(b, 'Xblob')
+  let br = readfile('Xblob', 'B')
+  call assert_equal(b, br)
+  call delete('Xblob')
+endfunc
+
+" filter() item in blob
+func Test_blob_filter()
+  let b = 0zDEADBEEF
+  call filter(b, 'v:val != 0xEF')
+  call assert_equal(0zDEADBE, b)
+endfunc
+
+" map() item in blob
+func Test_blob_map()
+  let b = 0zDEADBEEF
+  call map(b, 'v:val + 1')
+  call assert_equal(0zDFAEBFF0, b)
+endfunc
+
+func Test_blob_index()
+  call assert_equal(2, index(0zDEADBEEF, 0xBE))
+  call assert_equal(-1, index(0zDEADBEEF, 0))
+endfunc
+
+func Test_blob_insert()
+  let b = 0zDEADBEEF
+  call insert(b, 0x33)
+  call assert_equal(0z33DEADBEEF, b)
+
+  let b = 0zDEADBEEF
+  call insert(b, 0x33, 2)
+  call assert_equal(0zDEAD33BEEF, b)
+endfunc
+
+func Test_blob_reverse()
+  call assert_equal(0zEFBEADDE, reverse(0zDEADBEEF))
+  call assert_equal(0zBEADDE, reverse(0zDEADBE))
+  call assert_equal(0zADDE, reverse(0zDEAD))
+  call assert_equal(0zDE, reverse(0zDE))
+endfunc
+
+func Test_blob_json_encode()
+  call assert_equal('[222,173,190,239]', json_encode(0zDEADBEEF))
+  call assert_equal('[]', json_encode(0z))
+endfunc
diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim
index 8f4fb0f..d3f36f8 100644
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -516,6 +516,51 @@
   call assert_equal(1, found)
 endfunc
 
+func Test_raw_pipe_blob()
+  if !has('job')
+    return
+  endif
+  call ch_log('Test_raw_pipe_blob()')
+  " Add a dummy close callback to avoid that messages are dropped when calling
+  " ch_canread().
+  " Also test the non-blocking option.
+  let job = job_start(s:python . " test_channel_pipe.py",
+	\ {'mode': 'raw', 'drop': 'never', 'noblock': 1})
+  call assert_equal(v:t_job, type(job))
+  call assert_equal("run", job_status(job))
+
+  call assert_equal("open", ch_status(job))
+  call assert_equal("open", ch_status(job), {"part": "out"})
+
+  try
+    " Create a blob with the echo command and write it.
+    let blob = 0z00
+    let cmd = "echo something\n"
+    for i in range(0, len(cmd) - 1)
+      let blob[i] = char2nr(cmd[i])
+    endfor
+    call assert_equal(len(cmd), len(blob))
+    call ch_sendraw(job, blob)
+
+    " Read a blob with the reply.
+    let msg = ch_readblob(job)
+    let expected = 'something'
+    for i in range(0, len(expected) - 1)
+      call assert_equal(char2nr(expected[i]), msg[i])
+    endfor
+
+    let reply = ch_evalraw(job, "quit\n", {'timeout': 100})
+    call assert_equal("Goodbye!\n", substitute(reply, "\r", "", 'g'))
+  finally
+    call job_stop(job)
+  endtry
+
+  let g:Ch_job = job
+  call WaitForAssert({-> assert_equal("dead", job_status(g:Ch_job))})
+  let info = job_info(job)
+  call assert_equal("dead", info.status)
+endfunc
+
 func Test_nl_pipe()
   if !has('job')
     return