PUBLIC cl_int
clEnqueueWriteBuffer(cl_command_queue q, cl_mem obj, cl_bool blocking,
                     size_t offset, size_t size, const void *ptr,
                     cl_uint num_deps, const cl_event *deps,
                     cl_event *ev) try {
   validate_base(q, num_deps, deps);
   validate_obj(q, obj);

   if (!ptr || offset > obj->size() || offset + size > obj->size())
      throw error(CL_INVALID_VALUE);

   hard_event *hev = new hard_event(
      *q, CL_COMMAND_WRITE_BUFFER, { deps, deps + num_deps },
      soft_copy_op(q,
                   obj, { offset }, { 1 },
                   ptr, { 0 }, { 1 },
                   { size, 1, 1 }));

   ret_object(ev, hev);
   return CL_SUCCESS;

} catch (error &e) {
   return e.get();
}
PUBLIC cl_int
clGetMemObjectInfo(cl_mem obj, cl_mem_info param,
                   size_t size, void *buf, size_t *size_ret) {
   if (!obj)
      return CL_INVALID_MEM_OBJECT;

   switch (param) {
   case CL_MEM_TYPE:
      return scalar_property<cl_mem_object_type>(buf, size, size_ret,
                                                 obj->type());

   case CL_MEM_FLAGS:
      return scalar_property<cl_mem_flags>(buf, size, size_ret, obj->flags());

   case CL_MEM_SIZE:
      return scalar_property<size_t>(buf, size, size_ret, obj->size());

   case CL_MEM_HOST_PTR:
      return scalar_property<void *>(buf, size, size_ret, obj->host_ptr());

   case CL_MEM_MAP_COUNT:
      return scalar_property<cl_uint>(buf, size, size_ret, 0);

   case CL_MEM_REFERENCE_COUNT:
      return scalar_property<cl_uint>(buf, size, size_ret, obj->ref_count());

   case CL_MEM_CONTEXT:
      return scalar_property<cl_context>(buf, size, size_ret, &obj->ctx);

   case CL_MEM_ASSOCIATED_MEMOBJECT: {
      sub_buffer *sub = dynamic_cast<sub_buffer *>(obj);
      return scalar_property<cl_mem>(buf, size, size_ret,
                                     (sub ? &sub->parent : NULL));
   }
   case CL_MEM_OFFSET: {
      sub_buffer *sub = dynamic_cast<sub_buffer *>(obj);
      return scalar_property<size_t>(buf, size, size_ret,
                                     (sub ? sub->offset() : 0));
   }
   default:
      return CL_INVALID_VALUE;
   }
}