예제 #1
0
파일: mm.c 프로젝트: olikari/malloclab
/* $begin mmfree */
void mm_free(void *bp)
{
    size_t size = GET_SIZE(HDRP(bp));
    PUT(HDRP(bp), PACK(size, 0));
    PUT(FTRP(bp), PACK(size, 0));
	insert_block(coalesce(bp));
}
예제 #2
0
파일: parsing3.c 프로젝트: HARM67/lem-in
static int	read_block(t_app *app, char **array, unsigned int nbr)
{
	unsigned int	i;
	t_block			*n;

	i = 0;
	if (!verif_number(array[1]) || !verif_number(array[2]))
		return (0);
	if (array[0][0] == '#' || array[0][0] == 'L')
		return (0);
	n = new_block(array[0], ft_atoi(array[1]), ft_atoi(array[2]));
	insert_block(app, n);
	if (app->read_mode == 1)
	{
		app->have_start = 1;
		app->in = nbr;
	}
	else if (app->read_mode == 2)
	{
		app->have_end = 1;
		app->out = nbr;
	}
	if (app->read_mode == 1 || app->read_mode == 2)
		app->read_mode = 0;
	return (1);
}
예제 #3
0
파일: x86-common.c 프로젝트: KDE/kcmgrub2
void *
LRMI_alloc_real(int size)
{
	int i;
	char *r = (char *)REAL_MEM_BASE;

	if (!mem_info.ready)
		return NULL;

	if (mem_info.count == REAL_MEM_BLOCKS)
		return NULL;

	size = (size + 15) & ~15;

	for (i = 0; i < mem_info.count; i++) {
		if (mem_info.blocks[i].free && size < mem_info.blocks[i].size) {
			insert_block(i);

			mem_info.blocks[i].size = size;
			mem_info.blocks[i].free = 0;
			mem_info.blocks[i + 1].size -= size;

			return (void *)r;
		}

		r += mem_info.blocks[i].size;
	}

	return NULL;
}
예제 #4
0
/*
 * Allocate a page and add it to freelist of given pool.
 */
static int grow_pool(struct xv_pool *pool, gfp_t flags)
{
	struct page *page;
	struct block_header *block;

	page = alloc_page(flags);
	if (unlikely(!page))
		return -ENOMEM;

	stat_inc(&pool->total_pages);

	spin_lock(&pool->lock);
	block = get_ptr_atomic(page, 0);

	block->size = PAGE_SIZE - XV_ALIGN;
	set_flag(block, BLOCK_FREE);
	clear_flag(block, PREV_FREE);
	set_blockprev(block, 0);

	insert_block(pool, page, 0, block);

	put_ptr_atomic(block);
	spin_unlock(&pool->lock);

	return 0;
}
예제 #5
0
파일: mm_to_bcsr.c 프로젝트: alucas/StarPU
/* we add an element to the list of blocks, it is either added to an existing block or in a block specifically created if there was none */
static void insert_elem(tmp_block_t **block_list, unsigned abs_i, unsigned abs_j, float val, unsigned c, unsigned r)
{
	/* we are looking for the block that contains (abs_i, abs_j) (abs = absolute) */
	unsigned i,j;

	i = abs_i / c;
	j = abs_j / r;

	tmp_block_t *block;

	block = search_block(*block_list, i, j);

	if (!block) {
		/* the block does not exist yet */
		/* create it */
		block = create_block(c, r);

		block->i = i;
		block->j = j;
		
		//printf("create block %d %d !\n", i, j);

		/* insert it in the block list */
		insert_block(block, block_list, i, j);
	}

	/* now insert the value in the corresponding block */
	unsigned local_i, local_j, local_index;

	local_i = abs_i % c;
	local_j = abs_j % r;
	local_index = local_j * c + local_i;
	
	block->val[local_index] = val;
}
예제 #6
0
파일: shm_alloc.c 프로젝트: seco/pipelinedb
/*
 * ShmemDynFree
 */
void
ShmemDynFree(void *addr)
{
	if (!ShmemDynAddrIsValid(addr))
		elog(ERROR, "ShmemDynFree: invalid/double freeing (%p)", addr);

	insert_block(addr);
}
예제 #7
0
//Draws the matrix
void paint_matrix ()
{
	//Goes through matrix
	for( int i = 0; i < LC_MATRIX_HEIGHT; i++)
		for( int j = 0; j < LC_MATRIX_WIDTH; j++)
		{
			insert_block (j, i, game_matrix[i][j]);
		}//for
}//paint_matrix
예제 #8
0
파일: shm_alloc.c 프로젝트: seco/pipelinedb
static void
split_block(void *ptr, Size size)
{
	Size orig_size = get_size(ptr);
	void *new_block = (void *) ((intptr_t) ptr + size + sizeof(Header));
	Header *header = get_header(ptr);

	init_block(new_block, orig_size - size - sizeof(Header), false);
	insert_block(new_block);

	header->size = size;
	header->is_allocated = true;
}
예제 #9
0
파일: mm.c 프로젝트: olikari/malloclab
/* $begin mminit */
int mm_init(void) 
{
    /* create the initial empty heap */
    if ((heap_listp = mem_sbrk(6*WSIZE)) == NULL) {
        return -1;
    }
    PUT(heap_listp, 0);                       		 /* alignment padding */
    PUT(heap_listp+(1*WSIZE), PACK(OVERHEAD, 1));  	/* prologue header */
	PUT(heap_listp+(2*WSIZE), 0);
	PUT(heap_listp+(3*WSIZE), 0);
    PUT(heap_listp+(4*WSIZE), PACK(OVERHEAD, 1));  	/* prologue footer */ 
    PUT(heap_listp+(5*WSIZE), PACK(0, 1));   		/* epilogue header */
    heap_listp += (2*WSIZE);
	freeblockcount = 0;

	//mm_checkheap(1); 
	
	char* freeblock;
    /* Extend the empty heap with a free block of CHUNKSIZE bytes */
    if ((freeblock = extend_heap((CHUNKSIZE/WSIZE + 88))) == NULL) {
        return -1;
    }
	insert_block(freeblock);
	
	//mm_checkheap(1);

	/*printf("Remove-a block\n");
	remove_block(freeblock);
	mm_checkheap(1);*/

	/*char* anotherfree;
	anotherfree = extend_heap(CHUNKSIZE/WSIZE);
	printf("adda annari blokkinni\n");
	insert_block(anotherfree);*/

	/*char* temp;
	temp = extend_heap(CHUNKSIZE/WSIZE);
	printf("adda þriðju blokkinni\n");
	insert_block(temp);*/

	/*size_t predFirstBlock = GET(PRED(freeblock));
	size_t succFirstBlock = GET((char*)SUCC(freeblock));
	printf("FIRSTBLOCK, PRED: %x SUCC: %x\n", predFirstBlock, succFirstBlock);

	remove_block(freeblock);
	printf("Remove-a fremstu blokk\n");

	mm_checkheap(1);*/

	return 0;
}
예제 #10
0
BLOCK_LIST * find_block_combo(BLOCK_LIST *list, BLOCK *block, BLOCK_LIST *combo) {
  
  BLOCK_LIST *ptr;
  
  if (list == NULL) {
    return NULL;
  }
  
  if (block == NULL) {
    return NULL;
  }
  
  ptr = list;
  
  // For all blocks
  while (ptr != NULL) {
    
    // If it is touching and if it is the same color
    if (neighbor_block(block, (BLOCK *)ptr->data) && ((BLOCK *)ptr->data)->image == block->image) {
      
      // If it is not in the list
      if (g_slist_find(combo, ptr->data) == NULL) {
        
        // Remove the block
        //blocks = remove_block(blocks, ptr);
        
        // Put it in the list
        combo = insert_block(combo, ptr->data);
        
        // Find combo blocks
        combo = find_block_combo(list, (BLOCK *)ptr->data, combo);
        
      }
      
    }
    
    ptr = ptr->next;
      
  }
  
  return combo;
  
}
예제 #11
0
int QTextDocumentPrivate::insertBlock(const QChar &blockSeparator,
                                  int pos, int blockFormat, int charFormat, QTextUndoCommand::Operation op)
{
    Q_ASSERT(formats.format(blockFormat).isBlockFormat());
    Q_ASSERT(formats.format(charFormat).isCharFormat());
    Q_ASSERT(pos >= 0 && (pos < fragments.length() || (pos == 0 && fragments.length() == 0)));
    Q_ASSERT(isValidBlockSeparator(blockSeparator));

    beginEditBlock();

    int strPos = text.length();
    text.append(blockSeparator);
    const int fragment = insert_block(pos, strPos, charFormat, blockFormat, op, QTextUndoCommand::BlockRemoved);

    Q_ASSERT(blocks.length() == fragments.length());

    int b = blocks.findNode(pos);
    QTextBlockData *B = blocks.fragment(b);

    QTextUndoCommand c = { QTextUndoCommand::BlockInserted, true,
                           op, charFormat, strPos, pos, { blockFormat },
                           B->revision };

    appendUndoItem(c);
    Q_ASSERT(undoState == undoStack.size());

    // update revision numbers of the modified blocks. Close to the
    // truth, but we may want to special case breaking a block before
    // the first or behind the last character
    B->revision = undoState;
    b = blocks.next(b);
    if (b) {
        B = blocks.fragment(b);
        B->revision = undoState;
    }

    if (formats.charFormat(charFormat).objectIndex() == -1)
        needsEnsureMaximumBlockCount = true;

    endEditBlock();
    return fragment;
}
예제 #12
0
파일: mm.c 프로젝트: olikari/malloclab
/* $begin mmplace-proto */
static void *place(void *bp, size_t asize)
// $end mmplace-proto 
{
    size_t csize = GET_SIZE(HDRP(bp));
	void *alloBlock = bp;

    if ((csize - asize) >= (DSIZE + OVERHEAD)) { 
		PUT(HDRP(bp), PACK(asize, 1));
        PUT(FTRP(bp), PACK(asize, 1));
        bp = NEXT_BLKP(bp);
        PUT(HDRP(bp), PACK(csize-asize, 0));
        PUT(FTRP(bp), PACK(csize-asize, 0));
		// setjum free blokkina sem kom út úr splitti í free lista
		insert_block(bp);
    }
    else {
        PUT(HDRP(bp), PACK(csize, 1));
        PUT(FTRP(bp), PACK(csize, 1));
    }
	// skilum pointer á allocated blokkina
	return alloBlock;
}
예제 #13
0
파일: kmalloc.c 프로젝트: Detegr/tapiOS
static vptr_t *find_free_blocks(uint32_t amount)
{
	if(amount == 0) PANIC();
	for(uint32_t i=0; i<metadata->blocks_allocated; ++i)
	{
		struct blockinfo *bi = &metadata->blocks[i];
		if(!bi->reserved && bi->size >= amount)
		{
			if(amount < bi->size)
			{
				//kprintf("Splitting block %d %x, size: %d\n", i, bi->ptr, bi->size-amount);
				vptr_t *newptr = bi->ptr + ((bi->size - amount) * 0x1000);
				struct blockinfo *ret = insert_block(amount, newptr);
				ret->reserved=true;
				bi->size -= amount;
				return ret->ptr;
			}
			bi->reserved=true;
			return bi->ptr;
		}
	}
	return NULL;
}
예제 #14
0
파일: kmalloc.c 프로젝트: Detegr/tapiOS
void *kmalloc(size_t size)
{
	if(size==0) return NULL;
	if(!kheap_end)
	{
		kheap_end=(0xC0000000|kernel_end_addr)+0x1000; // Kernel heap starts where kernel code ends
		metadata=(struct metadata*)kalloc_page(kheap_end, true, true);
		metadata->blocks_allocated=0;
		metadata->max_blocks = (0x1000 - sizeof(struct metadata)) / sizeof(struct blockinfo);
		metadata->blocks = (struct blockinfo*)&metadata[1];
		metadata->next=NULL;
		kheap_end += 0x1000;
	}

	int amount;
	if(size % 0x1000 == 0) amount=size/0x1000;
	else amount=(size/0x1000)+1;
	if(amount==0) amount=1;

	vptr_t *hole = find_free_blocks(amount);
	if(hole)
	{
		if((vaddr_t)hole & 0x00000FFF) PANIC();
		return hole;
	}

	vptr_t* ret=NULL;
	for(int i=0; i<amount; ++i, kheap_end+=0x1000)
	{
		if(!ret) ret=kalloc_page(kheap_end, false, true);
		else kalloc_page(kheap_end, false, true);
	}
	struct blockinfo *bi=insert_block(amount, ret);
	bi->reserved=true;
	if((vaddr_t)ret & 0x00000FFF) PANIC();
	return ret;
}
예제 #15
0
파일: mm.c 프로젝트: olikari/malloclab
/*
 * mm_realloc - naive implementation of mm_realloc. The realloc() function shall change the 
 * size of the memory object pointed to by ptr to the size specified by size. The contents 
 * of the object shall remain unchanged up to the lesser of the new and old sizes. If the 
 * new size of the memory object would require movement of the object, the space for the 
 * previous instantiation of the object is freed. If the new size is larger, the contents 
 * of the newly allocated portion of the object are unspecified.
 */
void *mm_realloc(void *ptr, size_t size)
{
    void *newp;
	void *oldp = ptr;
    size_t oldSize = GET_SIZE(HDRP(oldp));
	//size_t newSize = ALIGN(size);
	size_t asize;
	//size_t isPrevFree = GET_ALLOC(FTRP(PREV_BLKP(oldp)));
	size_t isNextFree = GET_ALLOC(HDRP(NEXT_BLKP(oldp)));
	//size_t prevSize = GET_SIZE(FTRP(PREV_BLKP(oldp)));
	size_t nextSize = GET_SIZE(HDRP(NEXT_BLKP(oldp)));

	//mm_checkheap(0);

	if(ptr == NULL)
		return mm_malloc(size);
	else if(size == 0){
		mm_free(ptr);
		return NULL;
	} 
	else if(size == GET_SIZE(HDRP(ptr)))
		return ptr;		
	
    if (size <= DSIZE)
        asize = DSIZE + OVERHEAD;
    else 
       asize = DSIZE * ((size + (OVERHEAD) + (DSIZE-1)) / DSIZE);
	
	if(asize <= oldSize){
	  //printf("OldSize: %zu , asize: %zu\n", oldSize, asize); 
		
	  if(oldSize - asize < OVERHEAD){
			//printf("FER HINGAÐ\n");
			return oldp;
		}
		PUT(HDRP(oldp), PACK(asize, 1));
		PUT(FTRP(oldp), PACK(asize, 1));
		//printf("alloc minnkar og nýtir rest í fría blokk\n");
		PUT(HDRP(NEXT_BLKP(oldp)), PACK(oldSize - asize, 0));
		PUT(FTRP(NEXT_BLKP(oldp)), PACK(oldSize - asize, 0));
		insert_block(NEXT_BLKP(oldp));
	}
	/*else if(!isNextFree && (oldSize + nextSize) >= asize){
		if((oldSize + nextSize) - asize < OVERHEAD){
			PUT(HDRP(oldp), PACK(oldSize + nextSize, 1));
			PUT(FTRP(oldp), PACK(oldSize + nextSize, 1));
		}
		else{
			PUT(HDRP(oldp), PACK(asize, 1));
			PUT(FTRP(oldp), PACK(asize, 1));
			PUT(HDRP(NEXT_BLKP(oldp)), PACK((oldSize+nextSize)-asize, 0));
			PUT(FTRP(NEXT_BLKP(oldp)), PACK((oldSize+nextSize)-asize, 0));
			void *freeBlock = NEXT_BLKP(oldp);
			insert_block(freeBlock);
			return oldp;
		}
	}*/	

	// calculate the adjusted size of the request ????
	// ef adjustedSize <= oldsize, return ptr
	
	if ((newp = mm_malloc(size)) == NULL) {
        printf("ERROR: mm_malloc failed in mm_realloc\n");
        exit(1);
    }
    if (size < oldSize) {
        oldSize = size;
    }
    memcpy(newp, ptr, oldSize);
    mm_free(ptr);
    return newp;
}
예제 #16
0
/*
 * Free block identified with <page, offset>
 */
void xv_free(struct xv_pool *pool, struct page *page, u32 offset)
{
	void *page_start;
	struct block_header *block, *tmpblock;

	offset -= XV_ALIGN;

	spin_lock(&pool->lock);

	page_start = get_ptr_atomic(page, 0);
	block = (struct block_header *)((char *)page_start + offset);

	/* Catch double free bugs */
	BUG_ON(test_flag(block, BLOCK_FREE));

	block->size = ALIGN(block->size, XV_ALIGN);

	tmpblock = BLOCK_NEXT(block);
	if (offset + block->size + XV_ALIGN == PAGE_SIZE)
		tmpblock = NULL;

	/* Merge next block if its free */
	if (tmpblock && test_flag(tmpblock, BLOCK_FREE)) {
		/*
		 * Blocks smaller than XV_MIN_ALLOC_SIZE
		 * are not inserted in any free list.
		 */
		if (tmpblock->size >= XV_MIN_ALLOC_SIZE) {
			remove_block(pool, page,
				    offset + block->size + XV_ALIGN, tmpblock,
				    get_index_for_insert(tmpblock->size));
		}
		block->size += tmpblock->size + XV_ALIGN;
	}

	/* Merge previous block if its free */
	if (test_flag(block, PREV_FREE)) {
		tmpblock = (struct block_header *)((char *)(page_start) +
						get_blockprev(block));
		offset = offset - tmpblock->size - XV_ALIGN;

		if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
			remove_block(pool, page, offset, tmpblock,
				    get_index_for_insert(tmpblock->size));

		tmpblock->size += block->size + XV_ALIGN;
		block = tmpblock;
	}

	/* No used objects in this page. Free it. */
	if (block->size == PAGE_SIZE - XV_ALIGN) {
		put_ptr_atomic(page_start);
		spin_unlock(&pool->lock);

		__free_page(page);
		stat_dec(&pool->total_pages);
		return;
	}

	set_flag(block, BLOCK_FREE);
	if (block->size >= XV_MIN_ALLOC_SIZE)
		insert_block(pool, page, offset, block);

	if (offset + block->size + XV_ALIGN != PAGE_SIZE) {
		tmpblock = BLOCK_NEXT(block);
		set_flag(tmpblock, PREV_FREE);
		set_blockprev(tmpblock, offset);
	}

	put_ptr_atomic(page_start);
	spin_unlock(&pool->lock);
}
예제 #17
0
/**
 * xv_malloc - Allocate block of given size from pool.
 * @pool: pool to allocate from
 * @size: size of block to allocate
 * @page: page no. that holds the object
 * @offset: location of object within page
 *
 * On success, <page, offset> identifies block allocated
 * and 0 is returned. On failure, <page, offset> is set to
 * 0 and -ENOMEM is returned.
 *
 * Allocation requests with size > XV_MAX_ALLOC_SIZE will fail.
 */
int xv_malloc(struct xv_pool *pool, u32 size, struct page **page,
		u32 *offset, gfp_t flags)
{
	int error;
	u32 index, tmpsize, origsize, tmpoffset;
	struct block_header *block, *tmpblock;

	*page = NULL;
	*offset = 0;
	origsize = size;

	if (unlikely(!size || size > XV_MAX_ALLOC_SIZE))
		return -ENOMEM;

	size = ALIGN(size, XV_ALIGN);

	spin_lock(&pool->lock);

	index = find_block(pool, size, page, offset);

	if (!*page) {
		spin_unlock(&pool->lock);
		if (flags & GFP_NOWAIT)
			return -ENOMEM;
		error = grow_pool(pool, flags);
		if (unlikely(error))
			return error;

		spin_lock(&pool->lock);
		index = find_block(pool, size, page, offset);
	}

	if (!*page) {
		spin_unlock(&pool->lock);
		return -ENOMEM;
	}

	block = get_ptr_atomic(*page, *offset);

	remove_block(pool, *page, *offset, block, index);

	/* Split the block if required */
	tmpoffset = *offset + size + XV_ALIGN;
	tmpsize = block->size - size;
	tmpblock = (struct block_header *)((char *)block + size + XV_ALIGN);
	if (tmpsize) {
		tmpblock->size = tmpsize - XV_ALIGN;
		set_flag(tmpblock, BLOCK_FREE);
		clear_flag(tmpblock, PREV_FREE);

		set_blockprev(tmpblock, *offset);
		if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
			insert_block(pool, *page, tmpoffset, tmpblock);

		if (tmpoffset + XV_ALIGN + tmpblock->size != PAGE_SIZE) {
			tmpblock = BLOCK_NEXT(tmpblock);
			set_blockprev(tmpblock, tmpoffset);
		}
	} else {
		/* This block is exact fit */
		if (tmpoffset != PAGE_SIZE)
			clear_flag(tmpblock, PREV_FREE);
	}

	block->size = origsize;
	clear_flag(block, BLOCK_FREE);

	put_ptr_atomic(block);
	spin_unlock(&pool->lock);

	*offset += XV_ALIGN;

	return 0;
}
예제 #18
0
void QTextDocumentPrivate::move(int pos, int to, int length, QTextUndoCommand::Operation op)
{
    Q_ASSERT(to <= fragments.length() && to <= pos);
    Q_ASSERT(pos >= 0 && pos+length <= fragments.length());
    Q_ASSERT(blocks.length() == fragments.length());

    if (pos == to)
        return;

    const bool needsInsert = to != -1;

#if !defined(QT_NO_DEBUG)
    const bool startAndEndInSameFrame = (frameAt(pos) == frameAt(pos + length - 1));

    const bool endIsEndOfChildFrame = (isAncestorFrame(frameAt(pos), frameAt(pos + length - 1))
                                       && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame);

    const bool startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent
               = (text.at(find(pos)->stringPosition) == QTextBeginningOfFrame
                  && text.at(find(pos + length - 1)->stringPosition) == QTextEndOfFrame
                  && frameAt(pos)->parentFrame() == frameAt(pos + length - 1)->parentFrame());

    const bool isFirstTableCell = (qobject_cast<QTextTable *>(frameAt(pos + length - 1))
                                  && frameAt(pos + length - 1)->parentFrame() == frameAt(pos));

    Q_ASSERT(startAndEndInSameFrame || endIsEndOfChildFrame || startIsStartOfFrameAndEndIsEndOfFrameWithCommonParent || isFirstTableCell);
#endif

    beginEditBlock();

    split(pos);
    split(pos+length);

    uint dst = needsInsert ? fragments.findNode(to) : 0;
    uint dstKey = needsInsert ? fragments.position(dst) : 0;

    uint x = fragments.findNode(pos);
    uint end = fragments.findNode(pos+length);

    uint w = 0;
    while (x != end) {
        uint n = fragments.next(x);

        uint key = fragments.position(x);
        uint b = blocks.findNode(key+1);
        QTextBlockData *B = blocks.fragment(b);
        int blockRevision = B->revision;

        QTextFragmentData *X = fragments.fragment(x);
        QTextUndoCommand c = { QTextUndoCommand::Removed, true,
                               op, X->format, X->stringPosition, key, { X->size },
                               blockRevision };
        QTextUndoCommand cInsert = { QTextUndoCommand::Inserted, true,
                                     op, X->format, X->stringPosition, dstKey, { X->size },
                                     blockRevision };

        if (key+1 != blocks.position(b)) {
//	    qDebug("remove_string from %d length %d", key, X->size);
            Q_ASSERT(noBlockInString(text.mid(X->stringPosition, X->size)));
            w = remove_string(key, X->size, op);

            if (needsInsert) {
                insert_string(dstKey, X->stringPosition, X->size, X->format, op);
                dstKey += X->size;
            }
        } else {
//	    qDebug("remove_block at %d", key);
            Q_ASSERT(X->size == 1 && isValidBlockSeparator(text.at(X->stringPosition)));
            b = blocks.previous(b);
            B = 0;
            c.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockDeleted : QTextUndoCommand::BlockRemoved;
            w = remove_block(key, &c.blockFormat, QTextUndoCommand::BlockAdded, op);

            if (needsInsert) {
                insert_block(dstKey++, X->stringPosition, X->format, c.blockFormat, op, QTextUndoCommand::BlockRemoved);
                cInsert.command = blocks.size(b) == 1 ? QTextUndoCommand::BlockAdded : QTextUndoCommand::BlockInserted;
                cInsert.blockFormat = c.blockFormat;
            }
        }
        appendUndoItem(c);
        if (B)
            B->revision = undoState;
        x = n;

        if (needsInsert)
            appendUndoItem(cInsert);
    }
    if (w)
        unite(w);

    Q_ASSERT(blocks.length() == fragments.length());

    endEditBlock();
}
예제 #19
0
int QTextDocumentPrivate::undoRedo(bool undo)
{
    PMDEBUG("%s, undoState=%d, undoStack size=%d", undo ? "undo:" : "redo:", undoState, undoStack.size());
    if (!undoEnabled || (undo && undoState == 0) || (!undo && undoState == undoStack.size()))
        return -1;

    undoEnabled = false;
    beginEditBlock();
    while (1) {
        if (undo)
            --undoState;
        QTextUndoCommand &c = undoStack[undoState];
        int resetBlockRevision = c.pos;

	switch(c.command) {
        case QTextUndoCommand::Inserted:
            remove(c.pos, c.length, (QTextUndoCommand::Operation)c.operation);
            PMDEBUG("   erase: from %d, length %d", c.pos, c.length);
            c.command = QTextUndoCommand::Removed;
	    break;
        case QTextUndoCommand::Removed:
            PMDEBUG("   insert: format %d (from %d, length %d, strpos=%d)", c.format, c.pos, c.length, c.strPos);
            insert_string(c.pos, c.strPos, c.length, c.format, (QTextUndoCommand::Operation)c.operation);
            c.command = QTextUndoCommand::Inserted;
	    break;
	case QTextUndoCommand::BlockInserted:
	case QTextUndoCommand::BlockAdded:
            remove_block(c.pos, &c.blockFormat, c.command, (QTextUndoCommand::Operation)c.operation);
            PMDEBUG("   blockremove: from %d", c.pos);
	    if (c.command == QTextUndoCommand::BlockInserted)
		c.command = QTextUndoCommand::BlockRemoved;
	    else
		c.command = QTextUndoCommand::BlockDeleted;
	    break;
	case QTextUndoCommand::BlockRemoved:
	case QTextUndoCommand::BlockDeleted:
            PMDEBUG("   blockinsert: charformat %d blockformat %d (pos %d, strpos=%d)", c.format, c.blockFormat, c.pos, c.strPos);
            insert_block(c.pos, c.strPos, c.format, c.blockFormat, (QTextUndoCommand::Operation)c.operation, c.command);
            resetBlockRevision += 1;
	    if (c.command == QTextUndoCommand::BlockRemoved)
		c.command = QTextUndoCommand::BlockInserted;
	    else
		c.command = QTextUndoCommand::BlockAdded;
	    break;
	case QTextUndoCommand::CharFormatChanged: {
            resetBlockRevision = -1; // ## TODO
            PMDEBUG("   charFormat: format %d (from %d, length %d)", c.format, c.pos, c.length);
            FragmentIterator it = find(c.pos);
            Q_ASSERT(!it.atEnd());

            int oldFormat = it.value()->format;
            setCharFormat(c.pos, c.length, formats.charFormat(c.format));
            c.format = oldFormat;
	    break;
	}
	case QTextUndoCommand::BlockFormatChanged: {
            resetBlockRevision = -1; // ## TODO
            PMDEBUG("   blockformat: format %d pos %d", c.format, c.pos);
            QTextBlock it = blocksFind(c.pos);
            Q_ASSERT(it.isValid());

            int oldFormat = block(it)->format;
            block(it)->format = c.format;
            QTextBlockGroup *oldGroup = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(oldFormat)));
            QTextBlockGroup *group = qobject_cast<QTextBlockGroup *>(objectForFormat(formats.blockFormat(c.format)));
            c.format = oldFormat;
            if (group != oldGroup) {
                if (oldGroup)
                    oldGroup->blockRemoved(it);
                if (group)
                    group->blockInserted(it);
            } else if (group) {
                group->blockFormatChanged(it);
            }
            documentChange(it.position(), it.length());
	    break;
	}
	case QTextUndoCommand::GroupFormatChange: {
            resetBlockRevision = -1; // ## TODO
            PMDEBUG("   group format change");
            QTextObject *object = objectForIndex(c.objectIndex);
            int oldFormat = formats.objectFormatIndex(c.objectIndex);
            changeObjectFormat(object, c.format);
            c.format = oldFormat;
	    break;
	}
	case QTextUndoCommand::Custom:
            resetBlockRevision = -1; // ## TODO
            if (undo)
                c.custom->undo();
            else
                c.custom->redo();
	    break;
	default:
	    Q_ASSERT(false);
        }

        if (resetBlockRevision >= 0) {
            int b = blocks.findNode(resetBlockRevision);
            QTextBlockData *B = blocks.fragment(b);
            B->revision = c.revision;
        }

        if (undo) {
            if (undoState == 0 || !undoStack[undoState-1].block)
                break;
        } else {
            ++undoState;
            if (undoState == undoStack.size() || !undoStack[undoState-1].block)
                break;
        }
    }
    undoEnabled = true;
    int editPos = -1;
    if (docChangeFrom >= 0) {
        editPos = qMin(docChangeFrom + docChangeLength, length() - 1);
    }
    endEditBlock();
    emitUndoAvailable(isUndoAvailable());
    emitRedoAvailable(isRedoAvailable());
    return editPos;
}