public function marshalValue($value)
    {
        $type = gettype($value);

        // Handle string values.
        if ($type === 'string') {
            if ($value === '') {
                return $this->handleInvalid('empty strings are invalid');
            }

            return ['S' => $value];
        }

        // Handle number values.
        if ($type === 'integer'
            || $type === 'double'
            || $value instanceof NumberValue
        ) {
            return ['N' => (string) $value];
        }

        // Handle boolean values.
        if ($type === 'boolean') {
            return ['BOOL' => $value];
        }

        // Handle null values.
        if ($type === 'NULL') {
            return ['NULL' => true];
        }

        // Handle set values.
        if ($value instanceof SetValue) {
            if (count($value) === 0) {
                return $this->handleInvalid('empty sets are invalid');
            }
            $previousType = null;
            $data = [];
            foreach ($value as $v) {
                $marshaled = $this->marshalValue($v);
                $setType = key($marshaled);
                if (!$previousType) {
                    $previousType = $setType;
                } elseif ($setType !== $previousType) {
                    return $this->handleInvalid('sets must be uniform in type');
                }
                $data[] = current($marshaled);
            }

            return [$previousType . 'S' => array_values(array_unique($data))];
        }

        // Handle list and map values.
        $dbType = 'L';
        if ($value instanceof \stdClass) {
            $type = 'array';
            $dbType = 'M';
        }
        if ($type === 'array' || $value instanceof \Traversable) {
            $data = [];
            $index = 0;
            foreach ($value as $k => $v) {
                if ($v = $this->marshalValue($v)) {
                    $data[$k] = $v;
                    if ($dbType === 'L' && (!is_int($k) || $k != $index++)) {
                        $dbType = 'M';
                    }
                }
            }
            return [$dbType => $data];
        }

        // Handle binary values.
        if (is_resource($value) || $value instanceof StreamInterface) {
            $value = $this->binary($value);
        }
        if ($value instanceof BinaryValue) {
            return ['B' => (string) $value];
        }

        // Handle invalid values.
        return $this->handleInvalid('encountered unexpected value');
    }