diff --git a/cores/esp8266/umm_malloc/umm_malloc.cpp b/cores/esp8266/umm_malloc/umm_malloc.cpp
index a8b93505d6..cfc55c8dba 100644
--- a/cores/esp8266/umm_malloc/umm_malloc.cpp
+++ b/cores/esp8266/umm_malloc/umm_malloc.cpp
@@ -266,28 +266,68 @@ static umm_heap_context_t *umm_get_ptr_context(void *ptr) {
 
 /* ------------------------------------------------------------------------ */
 
-static uint16_t umm_blocks( size_t size ) {
+static uint16_t umm_blocks(size_t size) {
 
-  /*
-   * The calculation of the block size is not too difficult, but there are
-   * a few little things that we need to be mindful of.
-   *
-   * When a block removed from the free list, the space used by the free
-   * pointers is available for data. That's what the first calculation
-   * of size is doing.
-   */
+    /*
+     * The calculation of the block size is not too difficult, but there are
+     * a few little things that we need to be mindful of.
+     *
+     * When a block removed from the free list, the space used by the free
+     * pointers is available for data. That's what the first calculation
+     * of size is doing.
+     *
+     * We don't check for the special case of (size == 0) here as this needs
+     * special handling in the caller depending on context. For example when we
+     * realloc() a block to size 0 it should simply be freed.
+     *
+     * We do NOT need to check for allocating more blocks than the heap can
+     * possibly hold - the allocator figures this out for us.
+     *
+     * There are only two cases left to consider:
+     *
+     * 1. (size <= body)  Obviously this is just one block
+     * 2. (blocks > (2^15)) This should return ((2^15)) to force a
+     *                      failure when the allocator runs
+     *
+     * If the requested size is greater that 32677-2 blocks (max block index
+     * minus the overhead of the top and bottom bookkeeping blocks) then we
+     * will return an incorrectly truncated value when the result is cast to
+     * a uint16_t.
+     */
 
-  if( size <= (sizeof(((umm_block *)0)->body)) )
-    return( 1 );
+    if (size <= (sizeof(((umm_block *)0)->body))) {
+        return 1;
+    }
 
-  /*
-   * If it's for more than that, then we need to figure out the number of
-   * additional whole blocks the size of an umm_block are required.
-   */
+    /*
+     * If it's for more than that, then we need to figure out the number of
+     * additional whole blocks the size of an umm_block are required, so
+     * reduce the size request by the number of bytes in the body of the
+     * first block.
+     */
+
+    size -= (sizeof(((umm_block *)0)->body));
 
-  size -= ( 1 + (sizeof(((umm_block *)0)->body)) );
+    /* NOTE WELL that we take advantage of the fact that INT16_MAX is the
+     * number of blocks that we can index in 15 bits :-)
+     *
+     * The below expression looks wierd, but it's right. Assuming body
+     * size of 4 bytes and a block size of 8 bytes:
+     *
+     * BYTES (BYTES-BODY) (BYTES-BODY-1)/BLOCKSIZE BLOCKS
+     *     1        n/a                        n/a      1
+     *     5          1                          0      2
+     *    12          8                          0      2
+     *    13          9                          1      3
+     */
+
+    size_t blocks = (2 + ((size - 1) / sizeof(umm_block)));
+
+    if (blocks > (INT16_MAX)) {
+        blocks = INT16_MAX;
+    }
 
-  return( 2 + size/(sizeof(umm_block)) );
+    return (uint16_t)blocks;
 }
 
 /* ------------------------------------------------------------------------ */