Exemple #1
0
class IdentityDictStrategy(BaseDictStrategy, TypedDictStrategyMixin):
    erase, unerase = new_static_erasing_pair("IdentityDictStrategy")
    iter_erase, iter_unerase = new_static_erasing_pair("IdentityDictStrategyIterator")

    def get_empty_storage(self, space):
        return self.erase(OrderedDict())

    def wrap(self, w_key):
        return w_key

    def unwrap(self, w_key):
        return w_key
Exemple #2
0
class ObjectDictStrategy(BaseDictStrategy, TypedDictStrategyMixin):
    erase, unerase = new_static_erasing_pair("ObjectDictStrategy")
    iter_erase, iter_unerase = new_static_erasing_pair("ObjectDictStrategyIterator")

    def get_empty_storage(self, space):
        return self.erase(OrderedDict(space.eq_w, space.hash_w))

    def wrap(self, w_key):
        return w_key

    def unwrap(self, w_key):
        return w_key
Exemple #3
0
class SymbolHashmapStrategy(HashmapStrategy):
    import_from_mixin(UnwrappedHashmapStrategyMixin)

    erase, unerase = rerased.new_static_erasing_pair("symbol-hashmap-strategy")

    def is_correct_type(self, w_obj):
        return isinstance(w_obj, values.W_Symbol)

    def wrap(self, val):
        assert isinstance(val, values.W_Symbol)
        return val

    def unwrap(self, w_val):
        assert isinstance(w_val, values.W_Symbol)
        return w_val

    def rem(self, w_dict, w_key, env, cont):
        from pycket.interpreter import return_value
        if not w_dict.immutable():
            raise Exception("Expected an immutable hash table")

        new_keys = []
        new_vals = []
        for (k, v) in w_dict.hash_items():
            if k is w_key:
                continue
            new_keys.append(k)
            new_vals.append(v)

        assert isinstance(w_dict, W_EqualHashTable)
        new_table = W_EqualHashTable(new_keys, new_vals, True)
        return return_value(new_table, env, cont)
class ConstantStringStrategy(StringStrategy):
    erase, unerase = new_static_erasing_pair("constant")

    def str_w(self, storage):
        return self.unerase(storage)

    def liststr_w(self, storage):
        strvalue = self.unerase(storage)
        return [c for c in strvalue]

    def length(self, storage):
        return len(self.unerase(storage))

    def getitem(self, storage, idx):
        return self.unerase(storage)[idx]

    def getslice(self, space, storage, start, end):
        return space.newstr_fromstr(self.unerase(storage)[start:end])

    def hash(self, storage):
        return compute_hash(self.unerase(storage))

    def copy(self, storage):
        return storage

    def to_mutable(self, space, s):
        s.strategy = strategy = space.fromcache(MutableStringStrategy)
        s.str_storage = strategy.erase(self.liststr_w(s.str_storage))

    def extend_into(self, src_storage, dst_storage):
        dst_storage += self.unerase(src_storage)

    def mul(self, space, storage, times):
        return space.newstr_fromstr(self.unerase(storage) * times)
Exemple #5
0
class ObjectHashmapStrategy(HashmapStrategy):
    erase, unerase = rerased.new_static_erasing_pair(
        "object-hashmap-strategry")

    def get(self, w_dict, w_key, env, cont):
        return equal_hash_ref_loop(self.unerase(w_dict.hstorage), 0, w_key,
                                   env, cont)

    def set(self, w_dict, w_key, w_val, env, cont):
        return equal_hash_set_loop(self.unerase(w_dict.hstorage), 0, w_key,
                                   w_val, env, cont)

    def items(self, w_dict):
        return self.unerase(w_dict.hstorage)

    def get_item(self, w_dict, i):
        try:
            return self.unerase(w_dict.hstorage)[i]
        except IndexError:
            raise

    def length(self, w_dict):
        return len(self.unerase(w_dict.hstorage))

    def create_storage(self, keys, vals):
        return self.erase([(k, vals[i]) for i, k in enumerate(keys)])
Exemple #6
0
class EmptyHashmapStrategy(HashmapStrategy):
    erase, unerase = rerased.new_static_erasing_pair("object-hashmap-strategy")

    def get(self, w_dict, w_key, env, cont):
        from pycket.interpreter import return_value
        return return_value(w_missing, env, cont)  # contains nothing

    def set(self, w_dict, w_key, w_val, env, cont):
        self.switch_to_correct_strategy(w_dict, w_key)
        return w_dict.hash_set(w_key, w_val, env, cont)

    def rem(self, w_dict, w_key, env, cont):
        from pycket.interpreter import return_value
        return return_value(w_dict, env, cont)  # there's nothing to remove

    def _set(self, w_dict, w_key, w_val):
        self.switch_to_correct_strategy(w_dict, w_key)
        return w_dict._set(w_key, w_val)

    def rem_inplace(self, w_dict, w_key, env, cont):
        from pycket.interpreter import return_value
        return return_value(values.w_void, env,
                            cont)  # there's nothing to remove

    def items(self, w_dict):
        return []

    def get_item(self, w_dict, i):
        raise IndexError

    def length(self, w_dict):
        return 0

    def create_storage(self, keys, vals):
        assert not keys
        assert not vals
        return self.erase(None)

    def switch_to_correct_strategy(self, w_dict, w_key):
        if type(w_key) is values.W_Fixnum:
            strategy = FixnumHashmapStrategy.singleton
        elif type(w_key) is values.W_Symbol:
            strategy = SymbolHashmapStrategy.singleton
        elif isinstance(w_key, values_string.W_String):
            strategy = StringHashmapStrategy.singleton
        elif isinstance(w_key, values.W_ImmutableBytes):
            strategy = ImmutableByteHashmapStrategy.singleton
        elif isinstance(w_key, values.W_MutableBytes):
            strategy = MutableByteHashmapStrategy.singleton
        else:
            strategy = ObjectHashmapStrategy.singleton
        storage = strategy.create_storage([], [])
        w_dict.strategy = strategy
        w_dict.hstorage = storage
Exemple #7
0
class UnicodeStringStrategy(ImmutableStringStrategy):
    erase, unerase = rerased.new_static_erasing_pair("unicode-string-strategy")

    def make_mutable(self, w_str):
        strategy = UnicodeMutableStringStrategy.singleton
        storage = strategy.erase(self.as_unicharlist(w_str))
        w_str.change_strategy(strategy, storage)

    def as_str_ascii(self, w_str):
        raise ValueError  # XXX or check?

    def as_str_utf8(self, w_str):
        s = self.unerase(w_str.get_storage())
        assert s is not None
        return s.encode("utf-8")

    def as_unicode(self, w_str):
        return self.unerase(w_str.get_storage())

    # string operations

    def length(self, w_str):
        return len(self.unerase(w_str.get_storage()))

    def getitem(self, w_str, index):
        return self.unerase(w_str.get_storage())[index]

    def getslice(self, w_str, start, stop):
        v = self.unerase(w_str.get_storage())[start:stop]
        return W_MutableString(self, self.erase(v))

    def eq(self, w_str, w_other):
        if w_other.get_strategy() is self:
            return self.unerase(w_str.get_storage()) == self.unerase(
                w_other.get_storage())
        return ImmutableStringStrategy.eq(self, w_str, w_other)

    def upper(self, w_str):
        value = self.unerase(w_str.get_storage())
        builder = UnicodeBuilder(len(value))
        for i, ch in enumerate(value):
            builder.append(unichr(unicodedb.toupper(ord(ch))))
        return W_MutableString(self, self.erase(builder.build()))

    def lower(self, w_str):
        value = self.unerase(w_str.get_storage())
        builder = UnicodeBuilder(len(value))
        for i, ch in enumerate(value):
            builder.append(unichr(unicodedb.tolower(ord(ch))))
        return W_MutableString(self, self.erase(builder.build()))
Exemple #8
0
class SymbolHashmapStrategy(HashmapStrategy):
    import_from_mixin(UnwrappedHashmapStrategyMixin)

    erase, unerase = rerased.new_static_erasing_pair("symbol-hashmap-strategry")

    def is_correct_type(self, w_obj):
        return isinstance(w_obj, values.W_Symbol)

    def wrap(self, val):
        assert isinstance(val, values.W_Symbol)
        return val

    def unwrap(self, w_val):
        assert isinstance(w_val, values.W_Symbol)
        return w_val
Exemple #9
0
class FixnumHashmapStrategy(HashmapStrategy):
    import_from_mixin(UnwrappedHashmapStrategyMixin)

    erase, unerase = rerased.new_static_erasing_pair("fixnum-hashmap-strategy")

    def is_correct_type(self, w_obj):
        return isinstance(w_obj, values.W_Fixnum)

    def wrap(self, val):
        assert isinstance(val, int)
        return values.W_Fixnum(val)

    def unwrap(self, w_val):
        assert isinstance(w_val, values.W_Fixnum)
        return w_val.value
Exemple #10
0
class StringHashmapStrategy(HashmapStrategy):
    import_from_mixin(UnwrappedHashmapStrategyMixin)

    erase, unerase = rerased.new_static_erasing_pair("string-hashmap-strategry")

    def is_correct_type(self, w_obj):
        return isinstance(w_obj, values_string.W_String)

    def wrap(self, w_val):
        return w_val

    def unwrap(self, w_val):
        return w_val

    def _create_empty_dict(self):
        return r_dict(cmp_strings, hash_strings)
Exemple #11
0
class ImmutableByteHashmapStrategy(HashmapStrategy):
    import_from_mixin(UnwrappedHashmapStrategyMixin)

    erase, unerase = rerased.new_static_erasing_pair("byte-hashmap-strategry")

    def is_correct_type(self, w_obj):
        return isinstance(w_obj, values.W_ImmutableBytes)

    def wrap(self, val):
        return val

    def unwrap(self, w_val):
        assert isinstance(w_val, values.W_ImmutableBytes)
        return w_val

    def _create_empty_dict(self):
        return r_dict(cmp_immutable_bytes, hash_immutable_bytes)
Exemple #12
0
class AsciiStringStrategy(ImmutableStringStrategy):
    erase, unerase = rerased.new_static_erasing_pair("ascii-string-strategy")

    def make_mutable(self, w_str):
        strategy = AsciiMutableStringStrategy.singleton
        storage = strategy.erase(self.as_charlist_ascii(w_str))
        w_str.change_strategy(strategy, storage)

    def as_str_utf8(self, w_str):
        return self.unerase(w_str.get_storage())

    as_str_ascii = as_str_utf8

    def as_unicode(self, w_str):
        return unicode(self.unerase(w_str.get_storage()))  # change strategy?

    # string operations

    def length(self, w_str):
        return len(self.unerase(w_str.get_storage()))

    def getitem(self, w_str, index):
        return unichr(ord(self.unerase(w_str.get_storage())[index]))

    def getslice(self, w_str, start, stop):
        v = self.unerase(w_str.get_storage())[start:stop]
        return W_MutableString(self, self.erase(v))

    def eq(self, w_str, w_other):
        if w_other.get_strategy() is self:
            return self.unerase(w_str.get_storage()) == self.unerase(
                w_other.get_storage())
        return ImmutableStringStrategy.eq(self, w_str, w_other)

    def hash(self, w_str):
        return compute_hash(w_str.as_str_ascii())

    def upper(self, w_str):
        return W_String.fromascii(w_str.as_str_ascii().upper())

    def lower(self, w_str):
        return W_String.fromascii(w_str.as_str_ascii().lower())
Exemple #13
0
class UnicodeMutableStringStrategy(MutableStringStrategy):
    erase, unerase = rerased.new_static_erasing_pair(
        "unicode-mutable-string-strategy")

    def as_charlist_ascii(self, w_str):
        raise ValueError("can't convert")

    def as_charlist_utf8(self, w_str):
        return list(self.as_str_utf8(w_str))

    def as_str_utf8(self, w_str):
        return u''.join(self.unerase(w_str.get_storage())).encode('utf-8')

    def as_unicharlist(self, w_str):
        return self.unerase(w_str.get_storage())[:]

    def as_unicode(self, w_str):
        return u"".join(self.unerase(w_str.get_storage()))

    # string operations

    def length(self, w_str):
        return len(self.unerase(w_str.get_storage()))

    def getitem(self, w_str, index):
        return self.unerase(w_str.get_storage())[index]

    def getslice(self, w_str, start, stop):
        v = self.unerase(w_str.get_storage())[start:stop]
        return W_MutableString(self, self.erase(v))

    def eq(self, w_str, w_other):
        if w_other.get_strategy() is self:
            return self.unerase(w_str.get_storage()) == self.unerase(
                w_other.get_storage())
        return MutableStringStrategy.eq(self, w_str, w_other)

    # mutation operations

    def setitem(self, w_str, index, unichar):
        self.unerase(w_str.get_storage())[index] = unichar.value

    def setslice(self, w_str, index, w_from, fromstart, fromend):
        target = self.unerase(w_str.get_storage())
        # XXX inefficient
        for sourceindex in range(fromstart, fromend):
            target[index] = w_from.getitem(sourceindex)
            index += 1

    def upper(self, w_str):
        # copy paste from above, but the types are different
        value = self.unerase(w_str.get_storage())
        builder = UnicodeBuilder(len(value))
        for i, ch in enumerate(value):
            builder.append(unichr(unicodedb.toupper(ord(ch))))
        return W_MutableString(self, self.erase(list(builder.build())))

    def lower(self, w_str):
        value = self.unerase(w_str.get_storage())
        builder = UnicodeBuilder(len(value))
        for i, ch in enumerate(value):
            builder.append(unichr(unicodedb.tolower(ord(ch))))
        return W_MutableString(self, self.erase(list(builder.build())))
Exemple #14
0
class AsciiMutableStringStrategy(MutableStringStrategy):
    erase, unerase = rerased.new_static_erasing_pair(
        "ascii-mutable-string-strategy")

    def make_unicode(self, w_str):
        strategy = UnicodeMutableStringStrategy.singleton
        storage = strategy.erase(self.as_unicharlist(w_str))
        w_str.change_strategy(strategy, storage)

    def as_charlist_utf8(self, w_str):
        return self.unerase(w_str.get_storage())[:]

    def as_unicharlist(self, w_str):
        return [unichr(ord(c)) for c in self.unerase(w_str.get_storage())]

    def as_str_utf8(self, w_str):
        return "".join(self.unerase(w_str.get_storage()))

    as_str_ascii = as_str_utf8

    # string operations

    def length(self, w_str):
        return len(self.unerase(w_str.get_storage()))

    def getitem(self, w_str, index):
        return unichr(ord(self.unerase(w_str.get_storage())[index]))

    def getslice(self, w_str, start, stop):
        v = self.unerase(w_str.get_storage())[start:stop]
        return W_MutableString(self, self.erase(v))

    def eq(self, w_str, w_other):
        if w_other.get_strategy() is self:
            return self.unerase(w_str.get_storage()) == self.unerase(
                w_other.get_storage())
        return MutableStringStrategy.eq(self, w_str, w_other)

    # mutation operations

    def setitem(self, w_str, index, unichar):
        val = ord(unichar.value)
        if val < 128:
            self.unerase(w_str.get_storage())[index] = chr(val)
        else:
            self.make_unicode(w_str)
            return w_str.setitem(index, unichar)

    def setslice(self, w_str, index, w_from, fromstart, fromend):
        if w_from.get_strategy() is self:
            target = self.unerase(w_str.get_storage())
            # XXX inefficient
            for sourceindex in range(fromstart, fromend):
                char = ord(w_from.getitem(sourceindex))
                assert char < 128  # XXX
                target[index] = chr(char)
                index += 1
        else:
            self.make_unicode(w_str)
            return w_str.setslice(index, w_from, fromstart, fromend)

    def upper(self, w_str):
        # XXX inefficient
        return W_String.fromascii(w_str.as_str_ascii().upper())

    def lower(self, w_str):
        # XXX inefficient
        return W_String.fromascii(w_str.as_str_ascii().lower())
Exemple #15
0
class ObjectHashmapStrategy(HashmapStrategy):
    erase, unerase = rerased.new_static_erasing_pair(
        "object-hashmap-strategry")

    import_from_mixin(UnwrappedHashmapStrategyMixin)

    def get_bucket(self, w_dict, w_key, nonull=False):
        hash = tagged_hash(w_key)
        storage = self.get_storage(w_dict)
        bucket = storage.get(hash, None)
        if nonull and bucket is None:
            storage[hash] = bucket = []
        return bucket

    def get(self, w_dict, w_key, env, cont):
        from pycket.interpreter import return_value
        bucket = self.get_bucket(w_dict, w_key)
        if not bucket:
            return return_value(w_missing, env, cont)
        return equal_hash_ref_loop(bucket, 0, w_key, env, cont)

    def set(self, w_dict, w_key, w_val, env, cont):
        bucket = self.get_bucket(w_dict, w_key, nonull=True)
        return equal_hash_set_loop(bucket, 0, w_key, w_val, env, cont)

    def _set(self, w_dict, w_key, w_val):
        raise NotImplementedError(
            "Unsafe set not supported for ObjectHashmapStrategy")

    def items(self, w_dict):
        items = []
        storage = self.unerase(w_dict.hstorage)
        for bucket in storage.itervalues():
            for item in bucket:
                items.append(item)
        return items

    if sys.maxint == 2147483647:

        def get_item(self, w_dict, i):
            storage = self.unerase(w_dict.hstorage)
            for bucket in storage.itervalues():
                size = len(bucket)
                if size > i:
                    return bucket[i]
                i -= size
            raise IndexError

    else:

        @staticmethod
        def _valid_bucket(v):
            return bool(v[1])

        def get_item(self, w_dict, i):
            from pycket.hash.persistent_hash_map import MASK_32
            storage = self.unerase(w_dict.hstorage)
            assert i >= 0
            i = r_uint(i)
            index = i & MASK_32
            subindex = (i >> 32) & MASK_32

            bucket = get_dict_item(storage, index)[1]
            if bucket is None:
                raise IndexError
            return bucket[subindex]

        def hash_iterate_next(self, w_dict, pos):
            from pycket.hash.persistent_hash_map import MASK_32
            storage = self.unerase(w_dict.hstorage)
            i = r_uint(pos.value)
            assert i >= 0
            index = r_uint(i & MASK_32)
            subindex = r_uint((i >> 32) & MASK_32)

            bucket = get_dict_item(storage, index)[1]
            subindex += 1
            if subindex == r_uint(len(bucket)):
                subindex = r_uint(0)
                try:
                    next = next_valid_index(storage,
                                            intmask(index),
                                            valid=self._valid_bucket)
                except IndexError:
                    return values.w_false
                index = r_uint(next)

            next = intmask((subindex << r_uint(32)) | index)
            return values.wrap(next)

        def hash_iterate_first(self, w_dict):
            return next_valid_index(w_dict, 0, valid=self._valid_bucket)

    def length(self, w_dict):
        storage = self.unerase(w_dict.hstorage)
        size = 0
        for bucket in storage.itervalues():
            size += len(bucket)
        return size

    def create_storage(self, keys, vals):
        storage = {}
        for i, key in enumerate(keys):
            val = vals[i]
            hash = tagged_hash(key)
            bucket = storage.get(hash, None)
            if bucket is None:
                storage[hash] = bucket = []
            bucket.append((key, val))
        return self.erase(storage)
class MutableStringStrategy(StringStrategy):
    erase, unerase = new_static_erasing_pair("mutable")

    def str_w(self, storage):
        return "".join(self.unerase(storage))

    def liststr_w(self, storage):
        return self.unerase(storage)

    def length(self, storage):
        return len(self.unerase(storage))

    def getitem(self, storage, idx):
        return self.unerase(storage)[idx]

    def getslice(self, space, storage, start, end):
        return space.newstr_fromchars(self.unerase(storage)[start:end])

    def delslice(self, space, storage, start, end):
        del self.unerase(storage)[start:end]

    def hash(self, storage):
        storage = self.unerase(storage)
        length = len(storage)
        if length == 0:
            return -1
        x = ord(storage[0]) << 7
        i = 0
        while i < length:
            x = intmask((1000003 * x) ^ ord(storage[i]))
            i += 1
        x ^= length
        return intmask(x)

    def copy(self, storage):
        return self.erase(self.unerase(storage)[:])

    def to_mutable(self, space, s):
        pass

    def extend_into(self, src_storage, dst_storage):
        dst_storage += self.unerase(src_storage)

    def clear(self, s):
        storage = self.unerase(s.str_storage)
        del storage[:]

    def mul(self, space, storage, times):
        return space.newstr_fromchars(self.unerase(storage) * times)

    def reverse(self, storage):
        storage = self.unerase(storage)
        storage.reverse()

    def swapcase(self, storage):
        storage = self.unerase(storage)
        changed = False
        for i, c in enumerate(storage):
            if ord("A") <= ord(c) <= ord("Z"):
                new_c = c.lower()
                changed = True
            elif ord("a") <= ord(c) <= ord("z"):
                new_c = c.upper()
                changed = True
            else:
                new_c = c
            storage[i] = new_c
        return changed

    def downcase(self, storage):
        storage = self.unerase(storage)
        changed = False
        for i, c in enumerate(storage):
            new_c = c.lower()
            changed |= (c != new_c)
            storage[i] = new_c
        return changed

    def upcase(self, storage):
        storage = self.unerase(storage)
        changed = False
        for i, c in enumerate(storage):
            new_c = c.upper()
            changed |= (c != new_c)
            storage[i] = new_c
        return changed

    def capitalize(self, storage):
        storage = self.unerase(storage)
        changed = False
        for i, c in enumerate(storage):
            if i == 0:
                new_c = c.upper()
            else:
                new_c = c.lower()
            changed |= (c != new_c)
            storage[i] = new_c
        return changed

    def chomp(self, storage, newline=None):
        storage = self.unerase(storage)
        changed = False
        linebreaks = ["\n", "\r"]
        if len(storage) == 0:
            return changed
        elif newline is not None and len(newline) == 0:
            ch = storage[-1]
            i = len(storage) - 1
            if ch != "\r":
                while i >= 1 and ch in linebreaks:
                    i -= 1
                    ch = storage[i]
            if i < len(storage) - 1:
                i += 1
                changed = True
                if i > 0:
                    del storage[i:]
                else:
                    del storage[:]
        elif newline is not None and len(storage) >= len(newline):
            for i in xrange(len(newline) - 1, -1, -1):
                if newline[i] != storage[len(storage) - len(newline) + i]:
                    return changed
            start = len(storage) - len(newline)
            assert start >= 0
            del storage[start:]
            changed = True
        elif newline is None:
            ch = storage[-1]
            i = len(storage) - 1
            while i >= 0 and linebreaks and ch in linebreaks:
                linebreaks.remove(ch)
                i -= 1
                ch = storage[i]
            if i < len(storage) - 1:
                i += 1
                changed = True
                if i > 0:
                    del storage[i:]
                else:
                    del storage[:]
        return changed

    def chop(self, storage):
        storage = self.unerase(storage)
        if len(storage) == 0:
            return False
        elif storage[-1] == "\n" and len(storage) >= 2 and storage[-2] == "\r":
            idx = len(storage) - 2
            assert idx >= 0
            del storage[idx:]
            return True
        else:
            del storage[-1]
            return True

    def succ(self, storage):
        storage = self.unerase(storage)
        if len(storage) == 0:
            return

        carry = "\0"
        has_alnums = False
        last_alnum = 0
        start = len(storage) - 1

        while start >= 0:
            ch = storage[start]
            if ch in string.letters or ch in string.digits:
                has_alnums = True
                if ch == "9":
                    carry = "1"
                    storage[start] = "0"
                elif ch == "z":
                    carry = "a"
                    storage[start] = "a"
                elif ch == "Z":
                    carry = "A"
                    storage[start] = "A"
                else:
                    storage[start] = chr(ord(ch) + 1)
                    carry = "\0"

                if carry == "\0":
                    break
                last_alnum = start
            start -= 1

        if not has_alnums:
            start = len(storage) - 1
            carry = "\1"

            while start >= 0:
                ch = storage[start]
                if ord(ch) >= 255:
                    storage[start] = "\0"
                else:
                    storage[start] = chr(ord(ch) + 1)
                    break
                start -= 1

        if start < 0 and carry != "\0":
            last_alnum_ch = storage[last_alnum]
            storage[last_alnum] = carry
            storage.insert(last_alnum + 1, last_alnum_ch)

    def insert(self, storage, idx, other):
        storage = self.unerase(storage)
        for char in other:
            storage.insert(idx, char)
            idx += 1

    def replaceitem(self, storage, idx, char):
        storage = self.unerase(storage)
        storage[idx] = char

    def strip(self, storage):
        storage = self.unerase(storage)
        if not storage:
            return False

        shift = 0
        while shift < len(storage) and storage[shift].isspace():
            shift += 1
        if shift == len(storage):
            del storage[:]
            return True

        pop = len(storage)
        while pop > 0 and storage[pop - 1].isspace() or storage[pop -
                                                                1] == '\0':
            pop -= 1

        if pop < len(storage) or shift > 0:
            end = pop
            new_len = end - shift
            assert end >= 0
            assert new_len >= 0
            storage[0:new_len] = storage[shift:end]
            del storage[new_len:]
            return True
        else:
            return False