diff --git a/CHANGELOG.md b/CHANGELOG.md
index 518d4f55020d0914803814c07ebb079d1c389353..f7c54b10edb4fc9dd2192781dd2ab8a3dab06881 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 - `Tileset::source` for obtaining where the tileset actually came from.
 - `Tileset::columns`.
-- `layers::Layer::id`.
+- `Layer::id`, `Layer::width` and `Layer::height`.
 - Support for 'object'-type properties.
 - Documentation for map members.
 - Tests for `tiled_base64_zstandard.tmx`.
@@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
     - `Tile` has been moved into the `tile` module.
     - `Tileset` has been moved into the `tileset` module.
     - `Map::get_tileset_by_gid` -> `Map::tileset_by_gid`
+- `Layer::tiles` changed from `Vec<Vec<LayerTile>>` to `Vec<LayerTile>`.
 - Tile now has `image` instead of `images`. ([Issue comment](https://github.com/mapeditor/rs-tiled/issues/103#issuecomment-940773123))
 - Tileset now has `image` instead of `images`.
 - `Image::source` is now a `PathBuf` instead of a `String`.
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index bca6c44c5ec72403234ea0ee62de8cd17da4233e..0ad8f4cb323fb28dd0fc094b29e69c8caeacbbf0 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -1,2 +1,3 @@
 * Matthew Hall
 * Kevin Balz
+* Thorbjørn Lindeijer
\ No newline at end of file
diff --git a/src/layers.rs b/src/layers.rs
index 883426a03d4d2c47176f0d420c3982a022f1422b..2f801e2985eb1f58ce24e689c42c46a281f3e14a 100644
--- a/src/layers.rs
+++ b/src/layers.rs
@@ -45,6 +45,8 @@ impl LayerTile {
 #[derive(Debug, PartialEq, Clone)]
 pub struct Layer {
     pub name: String,
+    pub width: u32,
+    pub height: u32,
     pub opacity: f32,
     pub visible: bool,
     pub offset_x: f32,
@@ -63,11 +65,10 @@ impl Layer {
     pub(crate) fn new<R: Read>(
         parser: &mut EventReader<R>,
         attrs: Vec<OwnedAttribute>,
-        width: u32,
         layer_index: u32,
         infinite: bool,
     ) -> Result<Layer, TiledError> {
-        let ((o, v, ox, oy, n, id), ()) = get_attrs!(
+        let ((o, v, ox, oy, n, id), (w, h)) = get_attrs!(
             attrs,
             optionals: [
                 ("opacity", opacity, |v:String| v.parse().ok()),
@@ -77,9 +78,11 @@ impl Layer {
                 ("name", name, |v| Some(v)),
                 ("id", id, |v:String| v.parse().ok()),
             ],
-            required: [],
-            // this error should never happen since there are no required attrs
-            TiledError::MalformedAttributes("layer parsing error".to_string())
+            required: [
+                ("width", width, |v: String| v.parse().ok()),
+                ("height", height, |v: String| v.parse().ok()),
+            ],
+            TiledError::MalformedAttributes("layer parsing error, width and height attributes required".to_string())
         );
         let mut tiles: LayerData = LayerData::Finite(Default::default());
         let mut properties = HashMap::new();
@@ -88,7 +91,7 @@ impl Layer {
                 if infinite {
                     tiles = parse_infinite_data(parser, attrs)?;
                 } else {
-                    tiles = parse_data(parser, attrs, width)?;
+                    tiles = parse_data(parser, attrs)?;
                 }
                 Ok(())
             },
@@ -100,6 +103,8 @@ impl Layer {
 
         Ok(Layer {
             name: n.unwrap_or(String::new()),
+            width: w,
+            height: h,
             opacity: o.unwrap_or(1.0),
             visible: v.unwrap_or(true),
             offset_x: ox.unwrap_or(0.0),
@@ -111,9 +116,10 @@ impl Layer {
         })
     }
 }
+
 #[derive(Debug, PartialEq, Clone)]
 pub enum LayerData {
-    Finite(Vec<Vec<LayerTile>>),
+    Finite(Vec<LayerTile>),
     Infinite(HashMap<(i32, i32), Chunk>),
 }
 
@@ -185,7 +191,7 @@ pub struct Chunk {
     pub y: i32,
     pub width: u32,
     pub height: u32,
-    pub tiles: Vec<Vec<LayerTile>>,
+    pub tiles: Vec<LayerTile>,
 }
 
 impl Chunk {
@@ -207,7 +213,7 @@ impl Chunk {
             TiledError::MalformedAttributes("layer must have a name".to_string())
         );
 
-        let tiles = parse_data_line(encoding, compression, parser, width)?;
+        let tiles = parse_data_line(encoding, compression, parser)?;
 
         Ok(Chunk {
             x,
diff --git a/src/map.rs b/src/map.rs
index e22ff65f977b797ed717b5095d287249e3ff5110..52081e31c2a369c9ab10d2a954439e32eae61921 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -112,7 +112,7 @@ impl Map {
                 Ok(())
             },
             "layer" => |attrs| {
-                layers.push(Layer::new(parser, attrs, w, layer_index, infinite.unwrap_or(false))?);
+                layers.push(Layer::new(parser, attrs, layer_index, infinite.unwrap_or(false))?);
                 layer_index += 1;
                 Ok(())
             },
diff --git a/src/util.rs b/src/util.rs
index 50d28e5ebf87d4282ad27ef3b52fb54c677331c2..500cf3c8c091b66a1facfecd93f06b3b8cd37ee1 100644
--- a/src/util.rs
+++ b/src/util.rs
@@ -109,7 +109,6 @@ pub(crate) fn parse_infinite_data<R: Read>(
 pub(crate) fn parse_data<R: Read>(
     parser: &mut EventReader<R>,
     attrs: Vec<OwnedAttribute>,
-    width: u32,
 ) -> Result<LayerData, TiledError> {
     let ((e, c), ()) = get_attrs!(
         attrs,
@@ -121,7 +120,7 @@ pub(crate) fn parse_data<R: Read>(
         TiledError::MalformedAttributes("data must have an encoding and a compression".to_string())
     );
 
-    let tiles = parse_data_line(e, c, parser, width)?;
+    let tiles = parse_data_line(e, c, parser)?;
 
     Ok(LayerData::Finite(tiles))
 }
@@ -130,8 +129,7 @@ pub(crate) fn parse_data_line<R: Read>(
     encoding: Option<String>,
     compression: Option<String>,
     parser: &mut EventReader<R>,
-    width: u32,
-) -> Result<Vec<Vec<LayerTile>>, TiledError> {
+) -> Result<Vec<LayerTile>, TiledError> {
     match (encoding, compression) {
         (None, None) => {
             return Err(TiledError::Other(
@@ -139,26 +137,26 @@ pub(crate) fn parse_data_line<R: Read>(
             ))
         }
         (Some(e), None) => match e.as_ref() {
-            "base64" => return parse_base64(parser).map(|v| convert_to_tile(&v, width)),
-            "csv" => return decode_csv(width, parser),
+            "base64" => return parse_base64(parser).map(|v| convert_to_tiles(&v)),
+            "csv" => return decode_csv(parser),
             e => return Err(TiledError::Other(format!("Unknown encoding format {}", e))),
         },
         (Some(e), Some(c)) => match (e.as_ref(), c.as_ref()) {
             ("base64", "zlib") => {
                 return parse_base64(parser)
                     .and_then(decode_zlib)
-                    .map(|v| convert_to_tile(&v, width))
+                    .map(|v| convert_to_tiles(&v))
             }
             ("base64", "gzip") => {
                 return parse_base64(parser)
                     .and_then(decode_gzip)
-                    .map(|v| convert_to_tile(&v, width))
+                    .map(|v| convert_to_tiles(&v))
             }
             #[cfg(feature = "zstd")]
             ("base64", "zstd") => {
                 return parse_base64(parser)
                     .and_then(decode_zstd)
-                    .map(|v| convert_to_tile(&v, width))
+                    .map(|v| convert_to_tiles(&v))
             }
             (e, c) => {
                 return Err(TiledError::Other(format!(
@@ -225,24 +223,17 @@ pub(crate) fn decode_zstd(data: Vec<u8>) -> Result<Vec<u8>, TiledError> {
 }
 
 pub(crate) fn decode_csv<R: Read>(
-    width: u32,
     parser: &mut EventReader<R>,
-) -> Result<Vec<Vec<LayerTile>>, TiledError> {
+) -> Result<Vec<LayerTile>, TiledError> {
     loop {
         match parser.next().map_err(TiledError::XmlDecodingError)? {
             XmlEvent::Characters(s) => {
-                let mut tiles_it = s
-                    .split(&['\n', '\r', ','][0..])
-                    .filter(|v| v.trim() != "")
-                    .map(|v| v.parse().unwrap())
+                let tiles = s
+                    .split(',')
+                    .map(|v| v.trim().parse().unwrap())
                     .map(LayerTile::new)
-                    .peekable();
-                let mut rows = Vec::new();
-                while tiles_it.peek().is_some() {
-                    let row = tiles_it.by_ref().take(width as usize).collect();
-                    rows.push(row);
-                }
-                return Ok(rows);
+                    .collect();
+                return Ok(tiles);
             }
             XmlEvent::EndElement { name, .. } => {
                 if name.local_name == "data" {
@@ -254,20 +245,14 @@ pub(crate) fn decode_csv<R: Read>(
     }
 }
 
-pub(crate) fn convert_to_tile(all: &Vec<u8>, width: u32) -> Vec<Vec<LayerTile>> {
+pub(crate) fn convert_to_tiles(all: &Vec<u8>) -> Vec<LayerTile> {
     let mut data = Vec::new();
-    for chunk in all.chunks((width * 4) as usize) {
-        let mut row = Vec::new();
-        for i in 0..width {
-            let start: usize = i as usize * 4;
-            let n = ((chunk[start + 3] as u32) << 24)
-                + ((chunk[start + 2] as u32) << 16)
-                + ((chunk[start + 1] as u32) << 8)
-                + chunk[start] as u32;
-            let n = LayerTile::new(n);
-            row.push(n);
-        }
-        data.push(row);
+    for chunk in all.chunks_exact(4) {
+        let n = chunk[0] as u32
+            + ((chunk[1] as u32) << 8)
+            + ((chunk[2] as u32) << 16)
+            + ((chunk[3] as u32) << 24);
+        data.push(LayerTile::new(n));
     }
     data
 }
diff --git a/tests/lib.rs b/tests/lib.rs
index eb4aa6d7f5cb3381fbd695898d32e90b6013f4c0..061f4ed2a543d383dfce0f5c9659adb876ebc7ab 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -22,14 +22,12 @@ fn test_gzip_and_zlib_encoded_and_raw_are_the_same() {
     assert_eq!(z, zstd);
 
     if let LayerData::Finite(tiles) = &c.layers[0].tiles {
-        assert_eq!(tiles.len(), 100);
-        assert_eq!(tiles[0].len(), 100);
-        assert_eq!(tiles[99].len(), 100);
-        assert_eq!(tiles[0][0].gid, 35);
-        assert_eq!(tiles[1][0].gid, 17);
-        assert_eq!(tiles[2][0].gid, 0);
-        assert_eq!(tiles[2][1].gid, 17);
-        assert!(tiles[99].iter().map(|t| t.gid).all(|g| g == 0));
+        assert_eq!(tiles.len(), 100 * 100);
+        assert_eq!(tiles[0].gid, 35);
+        assert_eq!(tiles[100].gid, 17);
+        assert_eq!(tiles[200].gid, 0);
+        assert_eq!(tiles[200 + 1].gid, 17);
+        assert!(tiles[9900..9999].iter().map(|t| t.gid).all(|g| g == 0));
     } else {
         assert!(false, "It is wrongly recognised as an infinite map");
     }
@@ -163,10 +161,10 @@ fn test_flipped_gid() {
     let r = Map::parse_file("assets/tiled_flipped.tmx").unwrap();
 
     if let LayerData::Finite(tiles) = &r.layers[0].tiles {
-        let t1 = tiles[0][0];
-        let t2 = tiles[0][1];
-        let t3 = tiles[1][0];
-        let t4 = tiles[1][1];
+        let t1 = tiles[0];
+        let t2 = tiles[1];
+        let t3 = tiles[2];
+        let t4 = tiles[3];
         assert_eq!(t1.gid, t2.gid);
         assert_eq!(t2.gid, t3.gid);
         assert_eq!(t3.gid, t4.gid);
@@ -191,10 +189,9 @@ fn test_flipped_gid() {
 fn test_ldk_export() {
     let r = Map::parse_file("assets/ldk_tiled_export.tmx").unwrap();
     if let LayerData::Finite(tiles) = &r.layers[0].tiles {
-        assert_eq!(tiles.len(), 8);
-        assert_eq!(tiles[0].len(), 8);
-        assert_eq!(tiles[0][0].gid, 0);
-        assert_eq!(tiles[1][0].gid, 1);
+        assert_eq!(tiles.len(), 8 * 8);
+        assert_eq!(tiles[0].gid, 0);
+        assert_eq!(tiles[8].gid, 1);
     } else {
         assert!(false, "It is wrongly recognised as an infinite map");
     }