Buffer.BlockCopy(Array, Int32, Array, Int32, Int32) Método
Definição
Importante
Algumas informações dizem respeito a um produto pré-lançado que pode ser substancialmente modificado antes de ser lançado. A Microsoft não faz garantias, de forma expressa ou implícita, em relação à informação aqui apresentada.
Copia um número especificado de bytes de um array de origem a partir de um determinado offset para um array de destino que começa num determinado deslocamento.
public:
static void BlockCopy(Array ^ src, int srcOffset, Array ^ dst, int dstOffset, int count);
public static void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count);
static member BlockCopy : Array * int * Array * int * int -> unit
Public Shared Sub BlockCopy (src As Array, srcOffset As Integer, dst As Array, dstOffset As Integer, count As Integer)
Parâmetros
- src
- Array
O buffer de origem.
- srcOffset
- Int32
O deslocamento de bytes baseado em zero para src.
- dst
- Array
O amortecedor de destino.
- dstOffset
- Int32
O deslocamento de bytes baseado em zero para dst.
- count
- Int32
O número de bytes a copiar.
Exceções
src ou dst é null.
src ou dst não é um conjunto de primitivos.
-ou-
O número de bytes em src é menor que srcOffset mais count.
-ou-
O número de bytes em dst é menor que dstOffset mais count.
srcOffset, dstOffset, ou count é inferior a 0.
Exemplos
O exemplo seguinte copia regiões de arrays usando o BlockCopy método. Para cada BlockCopy operação, lista os arrays de origem e destino tanto como um array de valores como uma sequência de bytes. O exemplo ilustra a importância de considerar a endianidade de um sistema ao trabalhar com o método BlockCopy: Como Windows sistemas são little-endian, os bytes de ordem inferior do valor de um tipo de dado primitivo precedem os bytes de ordem superior.
using System;
class Example
{
// Display the individual bytes in the array in hexadecimal.
public static void DisplayArray(Array arr, string name)
{
Console.WindowWidth = 120;
Console.Write("{0,11}:", name);
for (int ctr = 0; ctr < arr.Length; ctr++)
{
byte[] bytes;
if (arr is long[])
bytes = BitConverter.GetBytes((long) arr.GetValue(ctr));
else
bytes = BitConverter.GetBytes((short) arr.GetValue(ctr));
foreach (byte byteValue in bytes)
Console.Write(" {0:X2}", byteValue);
}
Console.WriteLine();
}
// Display the individual array element values in hexadecimal.
public static void DisplayArrayValues(Array arr, string name)
{
// Get the length of one element in the array.
int elementLength = Buffer.ByteLength(arr) / arr.Length;
string formatString = String.Format(" {{0:X{0}}}", 2 * elementLength);
Console.Write( "{0,11}:", name);
for (int ctr = 0; ctr < arr.Length; ctr++)
Console.Write(formatString, arr.GetValue(ctr));
Console.WriteLine();
}
public static void Main( )
{
// These are the source and destination arrays for BlockCopy.
short[] src = { 258, 259, 260, 261, 262, 263, 264,
265, 266, 267, 268, 269, 270 };
long[] dest = { 17, 18, 19, 20 };
// Display the initial value of the arrays in memory.
Console.WriteLine( "Initial values of arrays:");
Console.WriteLine(" Array values as Bytes:");
DisplayArray(src, "src" );
DisplayArray(dest, "dest");
Console.WriteLine(" Array values:");
DisplayArrayValues(src, "src");
DisplayArrayValues(dest, "dest");
Console.WriteLine();
// Copy bytes 5-10 from source to index 7 in destination and display the result.
Buffer.BlockCopy(src, 5, dest, 7, 6);
Console.WriteLine("Buffer.BlockCopy(src, 5, dest, 7, 6 )");
Console.WriteLine(" Array values as Bytes:");
DisplayArray(src, "src");
DisplayArray(dest, "dest");
Console.WriteLine(" Array values:");
DisplayArrayValues(src, "src");
DisplayArrayValues(dest, "dest");
Console.WriteLine();
// Copy bytes 16-20 from source to index 22 in destination and display the result.
Buffer.BlockCopy(src, 16, dest, 22, 5);
Console.WriteLine("Buffer.BlockCopy(src, 16, dest, 22, 5)");
Console.WriteLine(" Array values as Bytes:");
DisplayArray(src, "src");
DisplayArray(dest, "dest");
Console.WriteLine(" Array values:");
DisplayArrayValues(src, "src");
DisplayArrayValues(dest, "dest");
Console.WriteLine();
// Copy overlapping range of bytes 4-10 to index 5 in source.
Buffer.BlockCopy(src, 4, src, 5, 7 );
Console.WriteLine("Buffer.BlockCopy( src, 4, src, 5, 7)");
Console.WriteLine(" Array values as Bytes:");
DisplayArray(src, "src");
DisplayArray(dest, "dest");
Console.WriteLine(" Array values:");
DisplayArrayValues(src, "src");
DisplayArrayValues(dest, "dest");
Console.WriteLine();
// Copy overlapping range of bytes 16-22 to index 15 in source.
Buffer.BlockCopy(src, 16, src, 15, 7);
Console.WriteLine("Buffer.BlockCopy( src, 16, src, 15, 7)");
Console.WriteLine(" Array values as Bytes:");
DisplayArray(src, "src");
DisplayArray(dest, "dest");
Console.WriteLine(" Array values:");
DisplayArrayValues(src, "src");
DisplayArrayValues(dest, "dest");
}
}
// The example displays the following output:
// Initial values of arrays:
// Array values as Bytes:
// src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 00 12 00 00 00 00 00 00 00 13 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
// Array values:
// src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
// dest: 0000000000000011 0000000000000012 0000000000000013 0000000000000014
//
// Buffer.BlockCopy(src, 5, dest, 7, 6 )
// Array values as Bytes:
// src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
// Array values:
// src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
// dest: 0100000000000011 0000000701060105 0000000000000013 0000000000000014
//
// Buffer.BlockCopy(src, 16, dest, 22, 5)
// Array values as Bytes:
// src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
// Array values:
// src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
// dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
//
// Buffer.BlockCopy( src, 4, src, 5, 7)
// Array values as Bytes:
// src: 02 01 03 01 04 04 01 05 01 06 01 07 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
// Array values:
// src: 0102 0103 0404 0501 0601 0701 0108 0109 010A 010B 010C 010D 010E
// dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
//
// Buffer.BlockCopy( src, 16, src, 15, 7)
// Array values as Bytes:
// src: 02 01 03 01 04 04 01 05 01 06 01 07 08 01 09 0A 01 0B 01 0C 01 0D 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
// Array values:
// src: 0102 0103 0404 0501 0601 0701 0108 0A09 0B01 0C01 0D01 010D 010E
// dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
open System
// Display the individual bytes in the array in hexadecimal.
let displayArray (arr: 'a []) (name: string) =
Console.WindowWidth <- 120
printf $"%11s{name}:"
for i = 0 to arr.Length - 1 do
let bytes =
match box arr with
| :? array<int64> ->
BitConverter.GetBytes(box arr[i] :?> int64)
| _ ->
BitConverter.GetBytes(box arr[i] :?> int16)
for byteValue in bytes do
printf $" %02X{byteValue}"
printfn ""
// Display the individual array element values in hexadecimal.
let inline displayArrayValues (arr: ^a []) name =
// Get the length of one element in the array.
let elementLength = Buffer.ByteLength arr / arr.Length
printf $"%11s{name}:"
for value in arr do
printf " %0*X" (2 * elementLength) value
printfn ""
// These are the source and destination arrays for BlockCopy.
let src =
[| 258s; 259s; 260s; 261s; 262s; 263s; 264s
265s; 266s; 267s; 268s; 269s; 270s |]
let dest =
[| 17L; 18L; 19L; 20L |]
// Display the initial value of the arrays in memory.
printfn "Initial values of arrays:"
printfn " Array values as Bytes:"
displayArray src "src"
displayArray dest "dest"
printfn " Array values:"
displayArrayValues src "src"
displayArrayValues dest "dest"
printfn ""
// Copy bytes 5-10 from source to index 7 in destination and display the result.
Buffer.BlockCopy(src, 5, dest, 7, 6)
printfn "Buffer.BlockCopy(src, 5, dest, 7, 6 )"
printfn " Array values as Bytes:"
displayArray src "src"
displayArray dest "dest"
printfn " Array values:"
displayArrayValues src "src"
displayArrayValues dest "dest"
printfn ""
// Copy bytes 16-20 from source to index 22 in destination and display the result.
Buffer.BlockCopy(src, 16, dest, 22, 5)
printfn "Buffer.BlockCopy(src, 16, dest, 22, 5)"
printfn " Array values as Bytes:"
displayArray src "src"
displayArray dest "dest"
printfn " Array values:"
displayArrayValues src "src"
displayArrayValues dest "dest"
printfn ""
// Copy overlapping range of bytes 4-10 to index 5 in source.
Buffer.BlockCopy(src, 4, src, 5, 7)
printfn "Buffer.BlockCopy(src, 4, src, 5, 7)"
printfn " Array values as Bytes:"
displayArray src "src"
displayArray dest "dest"
printfn " Array values:"
displayArrayValues src "src"
displayArrayValues dest "dest"
printfn ""
// Copy overlapping range of bytes 16-22 to index 15 in source.
Buffer.BlockCopy(src, 16, src, 15, 7)
printfn "Buffer.BlockCopy(src, 16, src, 15, 7)"
printfn " Array values as Bytes:"
displayArray src "src"
displayArray dest "dest"
printfn " Array values:"
displayArrayValues src "src"
displayArrayValues dest "dest"
// The example displays the following output:
// Initial values of arrays:
// Array values as Bytes:
// src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 00 12 00 00 00 00 00 00 00 13 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
// Array values:
// src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
// dest: 0000000000000011 0000000000000012 0000000000000013 0000000000000014
//
// Buffer.BlockCopy(src, 5, dest, 7, 6 )
// Array values as Bytes:
// src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
// Array values:
// src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
// dest: 0100000000000011 0000000701060105 0000000000000013 0000000000000014
//
// Buffer.BlockCopy(src, 16, dest, 22, 5)
// Array values as Bytes:
// src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
// Array values:
// src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
// dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
//
// Buffer.BlockCopy(src, 4, src, 5, 7)
// Array values as Bytes:
// src: 02 01 03 01 04 04 01 05 01 06 01 07 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
// Array values:
// src: 0102 0103 0404 0501 0601 0701 0108 0109 010A 010B 010C 010D 010E
// dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
//
// Buffer.BlockCopy(src, 16, src, 15, 7)
// Array values as Bytes:
// src: 02 01 03 01 04 04 01 05 01 06 01 07 08 01 09 0A 01 0B 01 0C 01 0D 0D 01 0E 01
// dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
// Array values:
// src: 0102 0103 0404 0501 0601 0701 0108 0A09 0B01 0C01 0D01 010D 010E
// dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
Module Example
' Display the individual bytes in the array in hexadecimal.
Sub DisplayArray(arr As Array, name As String)
Console.Write("{0,11}:", name)
For ctr As Integer = 0 to arr.Length - 1
Dim bytes() As Byte = BitConverter.GetBytes(arr(ctr))
For Each byteValue As Byte In bytes
Console.Write(" {0:X2}", byteValue)
Next
Next
Console.WriteLine()
End Sub
' Display the individual array element values in hexadecimal.
Sub DisplayArrayValues(arr As Array, name As String)
' Get the length of one element in the array.
Dim elementLength As Integer = Buffer.ByteLength(arr) / arr.Length
Dim formatString As String = String.Format(" {{0:X{0}}}", 2 * elementLength)
Console.Write("{0,11}:", name)
For ctr As Integer = 0 to arr.Length - 1
Console.Write(formatString, arr(ctr))
Next
Console.WriteLine()
End Sub
Sub Main()
Console.WindowWidth = 120
' These are source and destination arrays for BlockCopy.
Dim src() As Short = { 258, 259, 260, 261, 262, 263, 264, _
265, 266, 267, 268, 269, 270 }
Dim dest() As Long = { 17, 18, 19, 20 }
' Display the initial value of the arrays in memory.
Console.WriteLine( "Initial values of arrays:")
Console.WriteLine(" Array values as Bytes:")
DisplayArray(src, "src" )
DisplayArray(dest, "dest" )
Console.WriteLine(" Array values:")
DisplayArrayValues(src, "src")
DisplayArrayValues(dest, "dest")
Console.WriteLine()
' Copy bytes 5-10 from source to index 7 in destination and display the result.
Buffer.BlockCopy( src, 5, dest, 7, 6 )
Console.WriteLine("Buffer.BlockCopy(src, 5, dest, 7, 6 )")
Console.WriteLine(" Array values as Bytes:")
DisplayArray(src, "src")
DisplayArray(dest, "dest")
Console.WriteLine(" Array values:")
DisplayArrayValues(src, "src")
DisplayArrayValues(dest, "dest")
Console.WriteLine()
' Copy bytes 16-20 from source to index 22 in destination and display the result.
Buffer.BlockCopy( src, 16, dest, 22, 5 )
Console.WriteLine("Buffer.BlockCopy(src, 16, dest, 22, 5)")
Console.WriteLine(" Array values as Bytes:")
DisplayArray(src, "src")
DisplayArray(dest, "dest")
Console.WriteLine(" Array values:")
DisplayArrayValues(src, "src")
DisplayArrayValues(dest, "dest")
Console.WriteLine()
' Copy overlapping range of bytes 4-10 to index 5 in source.
Buffer.BlockCopy( src, 4, src, 5, 7 )
Console.WriteLine("Buffer.BlockCopy( src, 4, src, 5, 7)")
Console.WriteLine(" Array values as Bytes:")
DisplayArray(src, "src")
DisplayArray(dest, "dest")
Console.WriteLine(" Array values:")
DisplayArrayValues(src, "src")
DisplayArrayValues(dest, "dest")
Console.WriteLine()
' Copy overlapping range of bytes 16-22 to index 15 in source.
Buffer.BlockCopy(src, 16, src, 15, 7)
Console.WriteLine("Buffer.BlockCopy( src, 16, src, 15, 7)")
Console.WriteLine(" Array values as Bytes:")
DisplayArray(src, "src")
DisplayArray(dest, "dest")
Console.WriteLine(" Array values:")
DisplayArrayValues(src, "src")
DisplayArrayValues(dest, "dest")
End Sub
End Module
' This example displays the following output:
' Initial values of arrays:
' Array values as Bytes:
' src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
' dest: 11 00 00 00 00 00 00 00 12 00 00 00 00 00 00 00 13 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
' Array values:
' src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
' dest: 0000000000000011 0000000000000012 0000000000000013 0000000000000014
'
' Buffer.BlockCopy(src, 5, dest, 7, 6 )
' Array values as Bytes:
' src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
' dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 00 00 14 00 00 00 00 00 00 00
' Array values:
' src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
' dest: 0100000000000011 0000000701060105 0000000000000013 0000000000000014
'
' Buffer.BlockCopy(src, 16, dest, 22, 5)
' Array values as Bytes:
' src: 02 01 03 01 04 01 05 01 06 01 07 01 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
' dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
' Array values:
' src: 0102 0103 0104 0105 0106 0107 0108 0109 010A 010B 010C 010D 010E
' dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
'
' Buffer.BlockCopy( src, 4, src, 5, 7)
' Array values as Bytes:
' src: 02 01 03 01 04 04 01 05 01 06 01 07 08 01 09 01 0A 01 0B 01 0C 01 0D 01 0E 01
' dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
' Array values:
' src: 0102 0103 0404 0501 0601 0701 0108 0109 010A 010B 010C 010D 010E
' dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
'
' Buffer.BlockCopy( src, 16, src, 15, 7)
' Array values as Bytes:
' src: 02 01 03 01 04 04 01 05 01 06 01 07 08 01 09 0A 01 0B 01 0C 01 0D 0D 01 0E 01
' dest: 11 00 00 00 00 00 00 01 05 01 06 01 07 00 00 00 13 00 00 00 00 00 0A 01 0B 01 0C 00 00 00 00 00
' Array values:
' src: 0102 0103 0404 0501 0601 0701 0108 0A09 0B01 0C01 0D01 010D 010E
' dest: 0100000000000011 0000000701060105 010A000000000013 00000000000C010B
Observações
Este método copia bytes de count, começando em src, para srcOffset, começando em dst.dstOffset Tanto srcOffset como dstOffset são baseados em zero; ou seja, o primeiro byte de cada buffer está na posição 0, não na posição 1.
O BlockCopy método acede aos bytes no src array de parâmetros usando deslocamentos na memória, não programando construtos como índices ou limites superiores e inferiores do array. Por exemplo, se na linguagem de programação da sua aplicação declarar um Int32 array com um limite inferior zero de -50, e depois passar o array e um offset de 5 ao BlockCopy método, o primeiro elemento do array a que o método irá aceder é o segundo elemento do array, que está no índice -49. Além disso, qual o byte do elemento do array -49 é acedido primeiro depende da endianness do computador que está a executar a sua aplicação.
Como o nome indica, o BlockCopy método copia um bloco de bytes como um todo, em vez de copiar um byte de cada vez. Portanto, se src e referenciarem o mesmo array, e o intervalo dedstsrcOffset + -1 se sobrepõe ao intervalo decountdstOffset + -1, os valores dos bytes sobrepostos não são sobrescritos antes de serem copiados para o destino.count No exemplo seguinte, os valores dos bytes 0-16 num array nomeado arr são copiados para os bytes 12-28. Apesar do intervalo sobreposto, os valores dos bytes fonte são copiados com sucesso.
const int INT_SIZE = 4;
int[] arr = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
Buffer.BlockCopy(arr, 0 * INT_SIZE, arr, 3 * INT_SIZE, 4 * INT_SIZE);
foreach (int value in arr)
Console.Write("{0} ", value);
// The example displays the following output:
// 2 4 6 2 4 6 8 16 18 20
let intSize = 4
let arr = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
Buffer.BlockCopy(arr, 0 * intSize, arr, 3 * intSize, 4 * intSize)
for value in arr do
printf $"{value} "
// The example displays the following output:
// 2 4 6 2 4 6 8 16 18 20
Const INT_SIZE As Integer = 4
Dim arr() As Integer = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }
Buffer.BlockCopy(arr, 0 * INT_SIZE, arr, 3 * INT_SIZE, 4 * INT_SIZE)
For Each value As Integer In arr
Console.Write("{0} ", value)
Next
' The example displays the following output:
' 2 4 6 2 4 6 8 16 18 20
No exemplo seguinte, os valores dos bytes 12-28 num array nomeado arr são copiados para os bytes 0-16. Mais uma vez, apesar do intervalo sobreposto, os valores dos bytes de origem são copiados com sucesso.
const int INT_SIZE = 4;
int[] arr = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 };
Buffer.BlockCopy(arr, 3 * INT_SIZE, arr, 0 * INT_SIZE, 4 * INT_SIZE);
foreach (int value in arr)
Console.Write("{0} ", value);
// The example displays the following output:
// 8 10 12 14 10 12 14 16 18 20
let intSize = 4
let arr = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
Buffer.BlockCopy(arr, 3 * intSize, arr, 0 * intSize, 4 * intSize)
for value in arr do
printf $"{value} "
// The example displays the following output:
// 8 10 12 14 10 12 14 16 18 20
Const INT_SIZE As Integer = 4
Dim arr() As Integer = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }
Buffer.BlockCopy(arr, 3 * INT_SIZE, arr, 0 * INT_SIZE, 4 * INT_SIZE)
For Each value As Integer In arr
Console.Write("{0} ", value)
Next
' The example displays the following output:
' 8 10 12 14 10 12 14 16 18 20